1   /*
2    * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.print;
27  
28  import java.awt.BorderLayout;
29  import java.awt.Color;
30  import java.awt.Component;
31  import java.awt.Container;
32  import java.awt.Dialog;
33  import java.awt.FlowLayout;
34  import java.awt.Frame;
35  import java.awt.GraphicsConfiguration;
36  import java.awt.GridBagLayout;
37  import java.awt.GridBagConstraints;
38  import java.awt.GridLayout;
39  import java.awt.Insets;
40  import java.awt.Toolkit;
41  import java.awt.event.ActionEvent;
42  import java.awt.event.ActionListener;
43  import java.awt.event.FocusEvent;
44  import java.awt.event.FocusListener;
45  import java.awt.event.ItemEvent;
46  import java.awt.event.ItemListener;
47  import java.awt.event.WindowEvent;
48  import java.awt.event.WindowAdapter;
49  import java.io.File;
50  import java.io.FilePermission;
51  import java.io.IOException;
52  import java.net.URI;
53  import java.net.URL;
54  import java.text.DecimalFormat;
55  import java.util.Locale;
56  import java.util.ResourceBundle;
57  import java.util.Vector;
58  import javax.print.*;
59  import javax.print.attribute.*;
60  import javax.print.attribute.standard.*;
61  import javax.swing.*;
62  import javax.swing.border.Border;
63  import javax.swing.border.EmptyBorder;
64  import javax.swing.border.TitledBorder;
65  import javax.swing.event.ChangeEvent;
66  import javax.swing.event.ChangeListener;
67  import javax.swing.event.DocumentEvent;
68  import javax.swing.event.DocumentListener;
69  import javax.swing.event.PopupMenuEvent;
70  import javax.swing.event.PopupMenuListener;
71  import javax.swing.text.NumberFormatter;
72  import sun.print.SunPageSelection;
73  import java.awt.event.KeyEvent;
74  import java.net.URISyntaxException;
75  
76  
77  /**
78   * A class which implements a cross-platform print dialog.
79   *
80   * @author  Chris Campbell
81   */
82  public class ServiceDialog extends JDialog implements ActionListener {
83  
84      /**
85       * Waiting print status (user response pending).
86       */
87      public final static int WAITING = 0;
88  
89      /**
90       * Approve print status (user activated "Print" or "OK").
91       */
92      public final static int APPROVE = 1;
93  
94      /**
95       * Cancel print status (user activated "Cancel");
96       */
97      public final static int CANCEL = 2;
98  
99      private static final String strBundle = "sun.print.resources.serviceui";
100     private static final Insets panelInsets = new Insets(6, 6, 6, 6);
101     private static final Insets compInsets = new Insets(3, 6, 3, 6);
102 
103     private static ResourceBundle messageRB;
104     private JTabbedPane tpTabs;
105     private JButton btnCancel, btnApprove;
106     private PrintService[] services;
107     private int defaultServiceIndex;
108     private PrintRequestAttributeSet asOriginal;
109     private HashPrintRequestAttributeSet asCurrent;
110     private PrintService psCurrent;
111     private DocFlavor docFlavor;
112     private int status;
113 
114     private ValidatingFileChooser jfc;
115 
116     private GeneralPanel pnlGeneral;
117     private PageSetupPanel pnlPageSetup;
118     private AppearancePanel pnlAppearance;
119 
120     private boolean isAWT = false;
121 
122 
123     static {
124         initResource();
125     }
126 
127 
128     /**
129      * Constructor for the "standard" print dialog (containing all relevant
130      * tabs)
131      */
132     public ServiceDialog(GraphicsConfiguration gc,
133                          int x, int y,
134                          PrintService[] services,
135                          int defaultServiceIndex,
136                          DocFlavor flavor,
137                          PrintRequestAttributeSet attributes,
138                          Dialog dialog)
139     {
140         super(dialog, getMsg("dialog.printtitle"), true, gc);
141         initPrintDialog(x, y, services, defaultServiceIndex,
142                         flavor, attributes);
143     }
144 
145 
146 
147     /**
148      * Constructor for the "standard" print dialog (containing all relevant
149      * tabs)
150      */
151     public ServiceDialog(GraphicsConfiguration gc,
152                          int x, int y,
153                          PrintService[] services,
154                          int defaultServiceIndex,
155                          DocFlavor flavor,
156                          PrintRequestAttributeSet attributes,
157                          Frame frame)
158     {
159         super(frame, getMsg("dialog.printtitle"), true, gc);
160         initPrintDialog(x, y, services, defaultServiceIndex,
161                         flavor, attributes);
162     }
163 
164 
165     /**
166      * Initialize print dialog.
167      */
168     void initPrintDialog(int x, int y,
169                          PrintService[] services,
170                          int defaultServiceIndex,
171                          DocFlavor flavor,
172                          PrintRequestAttributeSet attributes)
173     {
174         this.services = services;
175         this.defaultServiceIndex = defaultServiceIndex;
176         this.asOriginal = attributes;
177         this.asCurrent = new HashPrintRequestAttributeSet(attributes);
178         this.psCurrent = services[defaultServiceIndex];
179         this.docFlavor = flavor;
180         SunPageSelection pages =
181             (SunPageSelection)attributes.get(SunPageSelection.class);
182         if (pages != null) {
183             isAWT = true;
184         }
185 
186         Container c = getContentPane();
187         c.setLayout(new BorderLayout());
188 
189         tpTabs = new JTabbedPane();
190         tpTabs.setBorder(new EmptyBorder(5, 5, 5, 5));
191 
192         String gkey = getMsg("tab.general");
193         int gmnemonic = getVKMnemonic("tab.general");
194         pnlGeneral = new GeneralPanel();
195         tpTabs.add(gkey, pnlGeneral);
196         tpTabs.setMnemonicAt(0, gmnemonic);
197 
198         String pkey = getMsg("tab.pagesetup");
199         int pmnemonic = getVKMnemonic("tab.pagesetup");
200         pnlPageSetup = new PageSetupPanel();
201         tpTabs.add(pkey, pnlPageSetup);
202         tpTabs.setMnemonicAt(1, pmnemonic);
203 
204         String akey = getMsg("tab.appearance");
205         int amnemonic = getVKMnemonic("tab.appearance");
206         pnlAppearance = new AppearancePanel();
207         tpTabs.add(akey, pnlAppearance);
208         tpTabs.setMnemonicAt(2, amnemonic);
209 
210         c.add(tpTabs, BorderLayout.CENTER);
211 
212         updatePanels();
213 
214         JPanel pnlSouth = new JPanel(new FlowLayout(FlowLayout.TRAILING));
215         btnApprove = createExitButton("button.print", this);
216         pnlSouth.add(btnApprove);
217         getRootPane().setDefaultButton(btnApprove);
218         btnCancel = createExitButton("button.cancel", this);
219         handleEscKey(btnCancel);
220         pnlSouth.add(btnCancel);
221         c.add(pnlSouth, BorderLayout.SOUTH);
222 
223         addWindowListener(new WindowAdapter() {
224             public void windowClosing(WindowEvent event) {
225                 dispose(CANCEL);
226             }
227         });
228 
229         getAccessibleContext().setAccessibleDescription(getMsg("dialog.printtitle"));
230         setResizable(false);
231         setLocation(x, y);
232         pack();
233     }
234 
235    /**
236      * Constructor for the solitary "page setup" dialog
237      */
238     public ServiceDialog(GraphicsConfiguration gc,
239                          int x, int y,
240                          PrintService ps,
241                          DocFlavor flavor,
242                          PrintRequestAttributeSet attributes,
243                          Dialog dialog)
244     {
245         super(dialog, getMsg("dialog.pstitle"), true, gc);
246         initPageDialog(x, y, ps, flavor, attributes);
247     }
248 
249     /**
250      * Constructor for the solitary "page setup" dialog
251      */
252     public ServiceDialog(GraphicsConfiguration gc,
253                          int x, int y,
254                          PrintService ps,
255                          DocFlavor flavor,
256                          PrintRequestAttributeSet attributes,
257                          Frame frame)
258     {
259         super(frame, getMsg("dialog.pstitle"), true, gc);
260         initPageDialog(x, y, ps, flavor, attributes);
261     }
262 
263 
264     /**
265      * Initialize "page setup" dialog
266      */
267     void initPageDialog(int x, int y,
268                          PrintService ps,
269                          DocFlavor flavor,
270                          PrintRequestAttributeSet attributes)
271     {
272         this.psCurrent = ps;
273         this.docFlavor = flavor;
274         this.asOriginal = attributes;
275         this.asCurrent = new HashPrintRequestAttributeSet(attributes);
276 
277         Container c = getContentPane();
278         c.setLayout(new BorderLayout());
279 
280         pnlPageSetup = new PageSetupPanel();
281         c.add(pnlPageSetup, BorderLayout.CENTER);
282 
283         pnlPageSetup.updateInfo();
284 
285         JPanel pnlSouth = new JPanel(new FlowLayout(FlowLayout.TRAILING));
286         btnApprove = createExitButton("button.ok", this);
287         pnlSouth.add(btnApprove);
288         getRootPane().setDefaultButton(btnApprove);
289         btnCancel = createExitButton("button.cancel", this);
290         handleEscKey(btnCancel);
291         pnlSouth.add(btnCancel);
292         c.add(pnlSouth, BorderLayout.SOUTH);
293 
294         addWindowListener(new WindowAdapter() {
295             public void windowClosing(WindowEvent event) {
296                 dispose(CANCEL);
297             }
298         });
299 
300         getAccessibleContext().setAccessibleDescription(getMsg("dialog.pstitle"));
301         setResizable(false);
302         setLocation(x, y);
303         pack();
304     }
305 
306     /**
307      * Performs Cancel when Esc key is pressed.
308      */
309     private void handleEscKey(JButton btnCancel) {
310         Action cancelKeyAction = new AbstractAction() {
311             public void actionPerformed(ActionEvent e) {
312                 dispose(CANCEL);
313             }
314         };
315         KeyStroke cancelKeyStroke =
316             KeyStroke.getKeyStroke((char)KeyEvent.VK_ESCAPE, 0);
317         InputMap inputMap =
318             btnCancel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
319         ActionMap actionMap = btnCancel.getActionMap();
320 
321         if (inputMap != null && actionMap != null) {
322             inputMap.put(cancelKeyStroke, "cancel");
323             actionMap.put("cancel", cancelKeyAction);
324         }
325     }
326 
327 
328     /**
329      * Returns the current status of the dialog (whether the user has selected
330      * the "Print" or "Cancel" button)
331      */
332     public int getStatus() {
333         return status;
334     }
335 
336     /**
337      * Returns an AttributeSet based on whether or not the user cancelled the
338      * dialog.  If the user selected "Print" we return their new selections,
339      * otherwise we return the attributes that were passed in initially.
340      */
341     public PrintRequestAttributeSet getAttributes() {
342         if (status == APPROVE) {
343             return asCurrent;
344         } else {
345             return asOriginal;
346         }
347     }
348 
349     /**
350      * Returns a PrintService based on whether or not the user cancelled the
351      * dialog.  If the user selected "Print" we return the user's selection
352      * for the PrintService, otherwise we return null.
353      */
354     public PrintService getPrintService() {
355         if (status == APPROVE) {
356             return psCurrent;
357         } else {
358             return null;
359         }
360     }
361 
362     /**
363      * Sets the current status flag for the dialog and disposes it (thus
364      * returning control of the parent frame back to the user)
365      */
366     public void dispose(int status) {
367         this.status = status;
368 
369         super.dispose();
370     }
371 
372     public void actionPerformed(ActionEvent e) {
373         Object source = e.getSource();
374         boolean approved = false;
375 
376         if (source == btnApprove) {
377             approved = true;
378 
379             if (pnlGeneral != null) {
380                 if (pnlGeneral.isPrintToFileRequested()) {
381                     approved = showFileChooser();
382                 } else {
383                     asCurrent.remove(Destination.class);
384                 }
385             }
386         }
387 
388         dispose(approved ? APPROVE : CANCEL);
389     }
390 
391     /**
392      * Displays a JFileChooser that allows the user to select the destination
393      * for "Print To File"
394      */
395     private boolean showFileChooser() {
396         Class dstCategory = Destination.class;
397 
398         Destination dst = (Destination)asCurrent.get(dstCategory);
399         if (dst == null) {
400             dst = (Destination)asOriginal.get(dstCategory);
401             if (dst == null) {
402                 dst = (Destination)psCurrent.getDefaultAttributeValue(dstCategory);
403                 // "dst" should not be null. The following code
404                 // is only added to safeguard against a possible
405                 // buggy implementation of a PrintService having a
406                 // null default Destination.
407                 if (dst == null) {
408                     try {
409                         dst = new Destination(new URI("file:out.prn"));
410                     } catch (URISyntaxException e) {
411                     }
412                 }
413             }
414         }
415 
416         File fileDest;
417         if (dst != null) {
418             try {
419                 fileDest = new File(dst.getURI());
420             } catch (Exception e) {
421                 // all manner of runtime exceptions possible
422                 fileDest = new File("out.prn");
423             }
424         } else {
425             fileDest = new File("out.prn");
426         }
427 
428         ValidatingFileChooser jfc = new ValidatingFileChooser();
429         jfc.setApproveButtonText(getMsg("button.ok"));
430         jfc.setDialogTitle(getMsg("dialog.printtofile"));
431         jfc.setSelectedFile(fileDest);
432 
433         int returnVal = jfc.showDialog(this, null);
434         if (returnVal == JFileChooser.APPROVE_OPTION) {
435             fileDest = jfc.getSelectedFile();
436 
437             try {
438                 asCurrent.add(new Destination(fileDest.toURI()));
439             } catch (Exception e) {
440                 asCurrent.remove(dstCategory);
441             }
442         } else {
443             asCurrent.remove(dstCategory);
444         }
445 
446         return (returnVal == JFileChooser.APPROVE_OPTION);
447     }
448 
449     /**
450      * Updates each of the top level panels
451      */
452     private void updatePanels() {
453         pnlGeneral.updateInfo();
454         pnlPageSetup.updateInfo();
455         pnlAppearance.updateInfo();
456     }
457 
458     /**
459      * Initialize ResourceBundle
460      */
461     public static void initResource() {
462         java.security.AccessController.doPrivileged(
463             new java.security.PrivilegedAction() {
464                 public Object run() {
465                     try {
466                         messageRB = ResourceBundle.getBundle(strBundle);
467                         return null;
468                     } catch (java.util.MissingResourceException e) {
469                         throw new Error("Fatal: Resource for ServiceUI " +
470                                         "is missing");
471                     }
472                 }
473             }
474         );
475     }
476 
477     /**
478      * Returns message string from resource
479      */
480     public static String getMsg(String key) {
481         try {
482             return messageRB.getString(key);
483         } catch (java.util.MissingResourceException e) {
484             throw new Error("Fatal: Resource for ServiceUI is broken; " +
485                             "there is no " + key + " key in resource");
486         }
487     }
488 
489     /**
490      * Returns mnemonic character from resource
491      */
492     private static char getMnemonic(String key) {
493         String str = getMsg(key + ".mnemonic");
494         if ((str != null) && (str.length() > 0)) {
495             return str.charAt(0);
496         } else {
497             return (char)0;
498         }
499     }
500 
501     /**
502      * Returns the mnemonic as a KeyEvent.VK constant from the resource.
503      */
504     private static int getVKMnemonic(String key) {
505         String str = getMsg(key + ".vkMnemonic");
506         if ((str != null) && (str.length() > 0)) {
507             try {
508                 return Integer.parseInt(str);
509             } catch (NumberFormatException nfe) {}
510         }
511         return 0;
512     }
513 
514     /**
515      * Returns URL for image resource
516      */
517     private static URL getImageResource(final String key) {
518         URL url = (URL)java.security.AccessController.doPrivileged(
519                        new java.security.PrivilegedAction() {
520                 public Object run() {
521                     URL url = ServiceDialog.class.getResource(
522                                                   "resources/" + key);
523                     return url;
524                 }
525         });
526 
527         if (url == null) {
528             throw new Error("Fatal: Resource for ServiceUI is broken; " +
529                             "there is no " + key + " key in resource");
530         }
531 
532         return url;
533     }
534 
535     /**
536      * Creates a new JButton and sets its text, mnemonic, and ActionListener
537      */
538     private static JButton createButton(String key, ActionListener al) {
539         JButton btn = new JButton(getMsg(key));
540         btn.setMnemonic(getMnemonic(key));
541         btn.addActionListener(al);
542 
543         return btn;
544     }
545 
546     /**
547      * Creates a new JButton and sets its text, and ActionListener
548      */
549     private static JButton createExitButton(String key, ActionListener al) {
550         String str = getMsg(key);
551         JButton btn = new JButton(str);
552         btn.addActionListener(al);
553         btn.getAccessibleContext().setAccessibleDescription(str);
554         return btn;
555     }
556 
557     /**
558      * Creates a new JCheckBox and sets its text, mnemonic, and ActionListener
559      */
560     private static JCheckBox createCheckBox(String key, ActionListener al) {
561         JCheckBox cb = new JCheckBox(getMsg(key));
562         cb.setMnemonic(getMnemonic(key));
563         cb.addActionListener(al);
564 
565         return cb;
566     }
567 
568     /**
569      * Creates a new JRadioButton and sets its text, mnemonic,
570      * and ActionListener
571      */
572     private static JRadioButton createRadioButton(String key,
573                                                   ActionListener al)
574     {
575         JRadioButton rb = new JRadioButton(getMsg(key));
576         rb.setMnemonic(getMnemonic(key));
577         rb.addActionListener(al);
578 
579         return rb;
580     }
581 
582   /**
583    * Creates a  pop-up dialog for "no print service"
584    */
585     public static void showNoPrintService(GraphicsConfiguration gc)
586     {
587         Frame dlgFrame = new Frame(gc);
588         JOptionPane.showMessageDialog(dlgFrame,
589                                       getMsg("dialog.noprintermsg"));
590         dlgFrame.dispose();
591     }
592 
593     /**
594      * Sets the constraints for the GridBagLayout and adds the Component
595      * to the given Container
596      */
597     private static void addToGB(Component comp, Container cont,
598                                 GridBagLayout gridbag,
599                                 GridBagConstraints constraints)
600     {
601         gridbag.setConstraints(comp, constraints);
602         cont.add(comp);
603     }
604 
605     /**
606      * Adds the AbstractButton to both the given ButtonGroup and Container
607      */
608     private static void addToBG(AbstractButton button, Container cont,
609                                 ButtonGroup bg)
610     {
611         bg.add(button);
612         cont.add(button);
613     }
614 
615 
616 
617 
618     /**
619      * The "General" tab.  Includes the controls for PrintService,
620      * PageRange, and Copies/Collate.
621      */
622     private class GeneralPanel extends JPanel {
623 
624         private PrintServicePanel pnlPrintService;
625         private PrintRangePanel pnlPrintRange;
626         private CopiesPanel pnlCopies;
627 
628         public GeneralPanel() {
629             super();
630 
631             GridBagLayout gridbag = new GridBagLayout();
632             GridBagConstraints c = new GridBagConstraints();
633 
634             setLayout(gridbag);
635 
636             c.fill = GridBagConstraints.BOTH;
637             c.insets = panelInsets;
638             c.weightx = 1.0;
639             c.weighty = 1.0;
640 
641             c.gridwidth = GridBagConstraints.REMAINDER;
642             pnlPrintService = new PrintServicePanel();
643             addToGB(pnlPrintService, this, gridbag, c);
644 
645             c.gridwidth = GridBagConstraints.RELATIVE;
646             pnlPrintRange = new PrintRangePanel();
647             addToGB(pnlPrintRange, this, gridbag, c);
648 
649             c.gridwidth = GridBagConstraints.REMAINDER;
650             pnlCopies = new CopiesPanel();
651             addToGB(pnlCopies, this, gridbag, c);
652         }
653 
654         public boolean isPrintToFileRequested() {
655             return (pnlPrintService.isPrintToFileSelected());
656         }
657 
658         public void updateInfo() {
659             pnlPrintService.updateInfo();
660             pnlPrintRange.updateInfo();
661             pnlCopies.updateInfo();
662         }
663     }
664 
665     private class PrintServicePanel extends JPanel
666         implements ActionListener, ItemListener, PopupMenuListener
667     {
668         private final String strTitle = getMsg("border.printservice");
669         private FilePermission printToFilePermission;
670         private JButton btnProperties;
671         private JCheckBox cbPrintToFile;
672         private JComboBox cbName;
673         private JLabel lblType, lblStatus, lblInfo;
674         private ServiceUIFactory uiFactory;
675         private boolean changedService = false;
676         private boolean filePermission;
677 
678         public PrintServicePanel() {
679             super();
680 
681             uiFactory = psCurrent.getServiceUIFactory();
682 
683             GridBagLayout gridbag = new GridBagLayout();
684             GridBagConstraints c = new GridBagConstraints();
685 
686             setLayout(gridbag);
687             setBorder(BorderFactory.createTitledBorder(strTitle));
688 
689             String[] psnames = new String[services.length];
690             for (int i = 0; i < psnames.length; i++) {
691                 psnames[i] = services[i].getName();
692             }
693             cbName = new JComboBox(psnames);
694             cbName.setSelectedIndex(defaultServiceIndex);
695             cbName.addItemListener(this);
696             cbName.addPopupMenuListener(this);
697 
698             c.fill = GridBagConstraints.BOTH;
699             c.insets = compInsets;
700 
701             c.weightx = 0.0;
702             JLabel lblName = new JLabel(getMsg("label.psname"), JLabel.TRAILING);
703             lblName.setDisplayedMnemonic(getMnemonic("label.psname"));
704             lblName.setLabelFor(cbName);
705             addToGB(lblName, this, gridbag, c);
706             c.weightx = 1.0;
707             c.gridwidth = GridBagConstraints.RELATIVE;
708             addToGB(cbName, this, gridbag, c);
709             c.weightx = 0.0;
710             c.gridwidth = GridBagConstraints.REMAINDER;
711             btnProperties = createButton("button.properties", this);
712             addToGB(btnProperties, this, gridbag, c);
713 
714             c.weighty = 1.0;
715             lblStatus = addLabel(getMsg("label.status"), gridbag, c);
716             lblStatus.setLabelFor(null);
717 
718             lblType = addLabel(getMsg("label.pstype"), gridbag, c);
719             lblType.setLabelFor(null);
720 
721             c.gridwidth = 1;
722             addToGB(new JLabel(getMsg("label.info"), JLabel.TRAILING),
723                     this, gridbag, c);
724             c.gridwidth = GridBagConstraints.RELATIVE;
725             lblInfo = new JLabel();
726             lblInfo.setLabelFor(null);
727 
728             addToGB(lblInfo, this, gridbag, c);
729 
730             c.gridwidth = GridBagConstraints.REMAINDER;
731             cbPrintToFile = createCheckBox("checkbox.printtofile", this);
732             addToGB(cbPrintToFile, this, gridbag, c);
733 
734             filePermission = allowedToPrintToFile();
735         }
736 
737         public boolean isPrintToFileSelected() {
738             return cbPrintToFile.isSelected();
739         }
740 
741         private JLabel addLabel(String text,
742                                 GridBagLayout gridbag, GridBagConstraints c)
743         {
744             c.gridwidth = 1;
745             addToGB(new JLabel(text, JLabel.TRAILING), this, gridbag, c);
746 
747             c.gridwidth = GridBagConstraints.REMAINDER;
748             JLabel label = new JLabel();
749             addToGB(label, this, gridbag, c);
750 
751             return label;
752         }
753 
754         public void actionPerformed(ActionEvent e) {
755             Object source = e.getSource();
756 
757             if (source == btnProperties) {
758                 if (uiFactory != null) {
759                     JDialog dialog = (JDialog)uiFactory.getUI(
760                                                ServiceUIFactory.MAIN_UIROLE,
761                                                ServiceUIFactory.JDIALOG_UI);
762 
763                     if (dialog != null) {
764                         dialog.show();
765                     } else {
766                         // REMIND: may want to notify the user why we're
767                         //         disabling the button
768                         btnProperties.setEnabled(false);
769                     }
770                 }
771             }
772         }
773 
774         public void itemStateChanged(ItemEvent e) {
775             if (e.getStateChange() == ItemEvent.SELECTED) {
776                 int index = cbName.getSelectedIndex();
777 
778                 if ((index >= 0) && (index < services.length)) {
779                     if (!services[index].equals(psCurrent)) {
780                         psCurrent = services[index];
781                         uiFactory = psCurrent.getServiceUIFactory();
782                         changedService = true;
783 
784                         Destination dest =
785                             (Destination)asOriginal.get(Destination.class);
786                         // to preserve the state of Print To File
787                         if ((dest != null || isPrintToFileSelected())
788                             && psCurrent.isAttributeCategorySupported(
789                                                         Destination.class)) {
790 
791                             if (dest != null) {
792                                 asCurrent.add(dest);
793                             } else {
794                                 dest = (Destination)psCurrent.
795                                     getDefaultAttributeValue(Destination.class);
796                                 // "dest" should not be null. The following code
797                                 // is only added to safeguard against a possible
798                                 // buggy implementation of a PrintService having a
799                                 // null default Destination.
800                                 if (dest == null) {
801                                     try {
802                                         dest =
803                                             new Destination(new URI("file:out.prn"));
804                                     } catch (URISyntaxException ue) {
805                                     }
806                                 }
807 
808                                 if (dest != null) {
809                                     asCurrent.add(dest);
810                                 }
811                             }
812                         } else {
813                             asCurrent.remove(Destination.class);
814                         }
815                     }
816                 }
817             }
818         }
819 
820         public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
821             changedService = false;
822         }
823 
824         public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
825             if (changedService) {
826                 changedService = false;
827                 updatePanels();
828             }
829         }
830 
831         public void popupMenuCanceled(PopupMenuEvent e) {
832         }
833 
834         /**
835          * We disable the "Print To File" checkbox if this returns false
836          */
837         private boolean allowedToPrintToFile() {
838             try {
839                 throwPrintToFile();
840                 return true;
841             } catch (SecurityException e) {
842                 return false;
843             }
844         }
845 
846         /**
847          * Break this out as it may be useful when we allow API to
848          * specify printing to a file. In that case its probably right
849          * to throw a SecurityException if the permission is not granted.
850          */
851         private void throwPrintToFile() {
852             SecurityManager security = System.getSecurityManager();
853             if (security != null) {
854                 if (printToFilePermission == null) {
855                     printToFilePermission =
856                         new FilePermission("<<ALL FILES>>", "read,write");
857                 }
858                 security.checkPermission(printToFilePermission);
859             }
860         }
861 
862         public void updateInfo() {
863             Class dstCategory = Destination.class;
864             boolean dstSupported = false;
865             boolean dstSelected = false;
866             boolean dstAllowed = filePermission ?
867                 allowedToPrintToFile() : false;
868 
869             // setup Destination (print-to-file) widgets
870             if (psCurrent.isAttributeCategorySupported(dstCategory)) {
871                 dstSupported = true;
872             }
873             Destination dst = (Destination)asCurrent.get(dstCategory);
874             if (dst != null) {
875                 dstSelected = true;
876             }
877             cbPrintToFile.setEnabled(dstSupported && dstAllowed);
878             cbPrintToFile.setSelected(dstSelected && dstAllowed
879                                       && dstSupported);
880 
881             // setup PrintService information widgets
882             Attribute type = psCurrent.getAttribute(PrinterMakeAndModel.class);
883             if (type != null) {
884                 lblType.setText(type.toString());
885             }
886             Attribute status =
887                 psCurrent.getAttribute(PrinterIsAcceptingJobs.class);
888             if (status != null) {
889                 lblStatus.setText(getMsg(status.toString()));
890             }
891             Attribute info = psCurrent.getAttribute(PrinterInfo.class);
892             if (info != null) {
893                 lblInfo.setText(info.toString());
894             }
895             btnProperties.setEnabled(uiFactory != null);
896         }
897     }
898 
899     private class PrintRangePanel extends JPanel
900         implements ActionListener, FocusListener
901     {
902         private final String strTitle = getMsg("border.printrange");
903         private final PageRanges prAll = new PageRanges(1, Integer.MAX_VALUE);
904         private JRadioButton rbAll, rbPages, rbSelect;
905         private JFormattedTextField tfRangeFrom, tfRangeTo;
906         private JLabel lblRangeTo;
907         private boolean prSupported;
908 
909         public PrintRangePanel() {
910             super();
911 
912             GridBagLayout gridbag = new GridBagLayout();
913             GridBagConstraints c = new GridBagConstraints();
914 
915             setLayout(gridbag);
916             setBorder(BorderFactory.createTitledBorder(strTitle));
917 
918             c.fill = GridBagConstraints.BOTH;
919             c.insets = compInsets;
920             c.gridwidth = GridBagConstraints.REMAINDER;
921 
922             ButtonGroup bg = new ButtonGroup();
923             JPanel pnlTop = new JPanel(new FlowLayout(FlowLayout.LEADING));
924             rbAll = createRadioButton("radiobutton.rangeall", this);
925             rbAll.setSelected(true);
926             bg.add(rbAll);
927             pnlTop.add(rbAll);
928             addToGB(pnlTop, this, gridbag, c);
929 
930             // Selection never seemed to work so I'm commenting this part.
931             /*
932             if (isAWT) {
933                 JPanel pnlMiddle  =
934                     new JPanel(new FlowLayout(FlowLayout.LEADING));
935                 rbSelect =
936                     createRadioButton("radiobutton.selection", this);
937                 bg.add(rbSelect);
938                 pnlMiddle.add(rbSelect);
939                 addToGB(pnlMiddle, this, gridbag, c);
940             }
941             */
942 
943             JPanel pnlBottom = new JPanel(new FlowLayout(FlowLayout.LEADING));
944             rbPages = createRadioButton("radiobutton.rangepages", this);
945             bg.add(rbPages);
946             pnlBottom.add(rbPages);
947             DecimalFormat format = new DecimalFormat("####0");
948             format.setMinimumFractionDigits(0);
949             format.setMaximumFractionDigits(0);
950             format.setMinimumIntegerDigits(0);
951             format.setMaximumIntegerDigits(5);
952             format.setParseIntegerOnly(true);
953             format.setDecimalSeparatorAlwaysShown(false);
954             NumberFormatter nf = new NumberFormatter(format);
955             nf.setMinimum(new Integer(1));
956             nf.setMaximum(new Integer(Integer.MAX_VALUE));
957             nf.setAllowsInvalid(true);
958             nf.setCommitsOnValidEdit(true);
959             tfRangeFrom = new JFormattedTextField(nf);
960             tfRangeFrom.setColumns(4);
961             tfRangeFrom.setEnabled(false);
962             tfRangeFrom.addActionListener(this);
963             tfRangeFrom.addFocusListener(this);
964             tfRangeFrom.setFocusLostBehavior(
965                 JFormattedTextField.PERSIST);
966             tfRangeFrom.getAccessibleContext().setAccessibleName(
967                                           getMsg("radiobutton.rangepages"));
968             pnlBottom.add(tfRangeFrom);
969             lblRangeTo = new JLabel(getMsg("label.rangeto"));
970             lblRangeTo.setEnabled(false);
971             pnlBottom.add(lblRangeTo);
972             NumberFormatter nfto;
973             try {
974                 nfto = (NumberFormatter)nf.clone();
975             } catch (CloneNotSupportedException e) {
976                 nfto = new NumberFormatter();
977             }
978             tfRangeTo = new JFormattedTextField(nfto);
979             tfRangeTo.setColumns(4);
980             tfRangeTo.setEnabled(false);
981             tfRangeTo.addFocusListener(this);
982             tfRangeTo.getAccessibleContext().setAccessibleName(
983                                           getMsg("label.rangeto"));
984             pnlBottom.add(tfRangeTo);
985             addToGB(pnlBottom, this, gridbag, c);
986         }
987 
988         public void actionPerformed(ActionEvent e) {
989             Object source = e.getSource();
990             SunPageSelection select = SunPageSelection.ALL;
991 
992             setupRangeWidgets();
993 
994             if (source == rbAll) {
995                 asCurrent.add(prAll);
996             } else if (source == rbSelect) {
997                 select = SunPageSelection.SELECTION;
998             } else if (source == rbPages ||
999                        source == tfRangeFrom ||
1000                        source == tfRangeTo) {
1001                 updateRangeAttribute();
1002                 select = SunPageSelection.RANGE;
1003             }
1004 
1005             if (isAWT) {
1006                 asCurrent.add(select);
1007             }
1008         }
1009 
1010         public void focusLost(FocusEvent e) {
1011             Object source = e.getSource();
1012 
1013             if ((source == tfRangeFrom) || (source == tfRangeTo)) {
1014                 updateRangeAttribute();
1015             }
1016         }
1017 
1018         public void focusGained(FocusEvent e) {}
1019 
1020         private void setupRangeWidgets() {
1021             boolean rangeEnabled = (rbPages.isSelected() && prSupported);
1022             tfRangeFrom.setEnabled(rangeEnabled);
1023             tfRangeTo.setEnabled(rangeEnabled);
1024             lblRangeTo.setEnabled(rangeEnabled);
1025         }
1026 
1027         private void updateRangeAttribute() {
1028             String strFrom = tfRangeFrom.getText();
1029             String strTo = tfRangeTo.getText();
1030 
1031             int min;
1032             int max;
1033 
1034             try {
1035                 min = Integer.parseInt(strFrom);
1036             } catch (NumberFormatException e) {
1037                 min = 1;
1038             }
1039 
1040             try {
1041                 max = Integer.parseInt(strTo);
1042             } catch (NumberFormatException e) {
1043                 max = min;
1044             }
1045 
1046             if (min < 1) {
1047                 min = 1;
1048                 tfRangeFrom.setValue(new Integer(1));
1049             }
1050 
1051             if (max < min) {
1052                 max = min;
1053                 tfRangeTo.setValue(new Integer(min));
1054             }
1055 
1056             PageRanges pr = new PageRanges(min, max);
1057             asCurrent.add(pr);
1058         }
1059 
1060         public void updateInfo() {
1061             Class prCategory = PageRanges.class;
1062             prSupported = false;
1063 
1064             if (psCurrent.isAttributeCategorySupported(prCategory) ||
1065                    isAWT) {
1066                 prSupported = true;
1067             }
1068 
1069             SunPageSelection select = SunPageSelection.ALL;
1070             int min = 1;
1071             int max = 1;
1072 
1073             PageRanges pr = (PageRanges)asCurrent.get(prCategory);
1074             if (pr != null) {
1075                 if (!pr.equals(prAll)) {
1076                     select = SunPageSelection.RANGE;
1077 
1078                     int[][] members = pr.getMembers();
1079                     if ((members.length > 0) &&
1080                         (members[0].length > 1)) {
1081                         min = members[0][0];
1082                         max = members[0][1];
1083                     }
1084                 }
1085             }
1086 
1087             if (isAWT) {
1088                 select = (SunPageSelection)asCurrent.get(
1089                                                 SunPageSelection.class);
1090             }
1091 
1092             if (select == SunPageSelection.ALL) {
1093                 rbAll.setSelected(true);
1094             } else if (select == SunPageSelection.SELECTION) {
1095                 // Comment this for now -  rbSelect is not initialized
1096                 // because Selection button is not added.
1097                 // See PrintRangePanel above.
1098 
1099                 //rbSelect.setSelected(true);
1100             } else { // RANGE
1101                 rbPages.setSelected(true);
1102             }
1103             tfRangeFrom.setValue(new Integer(min));
1104             tfRangeTo.setValue(new Integer(max));
1105             rbAll.setEnabled(prSupported);
1106             rbPages.setEnabled(prSupported);
1107             setupRangeWidgets();
1108         }
1109     }
1110 
1111     private class CopiesPanel extends JPanel
1112         implements ActionListener, ChangeListener
1113     {
1114         private final String strTitle = getMsg("border.copies");
1115         private SpinnerNumberModel snModel;
1116         private JSpinner spinCopies;
1117         private JLabel lblCopies;
1118         private JCheckBox cbCollate;
1119         private boolean scSupported;
1120 
1121         public CopiesPanel() {
1122             super();
1123 
1124             GridBagLayout gridbag = new GridBagLayout();
1125             GridBagConstraints c = new GridBagConstraints();
1126 
1127             setLayout(gridbag);
1128             setBorder(BorderFactory.createTitledBorder(strTitle));
1129 
1130             c.fill = GridBagConstraints.HORIZONTAL;
1131             c.insets = compInsets;
1132 
1133             lblCopies = new JLabel(getMsg("label.numcopies"), JLabel.TRAILING);
1134             lblCopies.setDisplayedMnemonic(getMnemonic("label.numcopies"));
1135             lblCopies.getAccessibleContext().setAccessibleName(
1136                                              getMsg("label.numcopies"));
1137             addToGB(lblCopies, this, gridbag, c);
1138 
1139             snModel = new SpinnerNumberModel(1, 1, 999, 1);
1140             spinCopies = new JSpinner(snModel);
1141             lblCopies.setLabelFor(spinCopies);
1142             // REMIND
1143             ((JSpinner.NumberEditor)spinCopies.getEditor()).getTextField().setColumns(3);
1144             spinCopies.addChangeListener(this);
1145             c.gridwidth = GridBagConstraints.REMAINDER;
1146             addToGB(spinCopies, this, gridbag, c);
1147 
1148             cbCollate = createCheckBox("checkbox.collate", this);
1149             cbCollate.setEnabled(false);
1150             addToGB(cbCollate, this, gridbag, c);
1151         }
1152 
1153         public void actionPerformed(ActionEvent e) {
1154             if (cbCollate.isSelected()) {
1155                 asCurrent.add(SheetCollate.COLLATED);
1156             } else {
1157                 asCurrent.add(SheetCollate.UNCOLLATED);
1158             }
1159         }
1160 
1161         public void stateChanged(ChangeEvent e) {
1162             updateCollateCB();
1163 
1164             asCurrent.add(new Copies(snModel.getNumber().intValue()));
1165         }
1166 
1167         private void updateCollateCB() {
1168             int num = snModel.getNumber().intValue();
1169             if (isAWT) {
1170                 cbCollate.setEnabled(true);
1171             } else {
1172                 cbCollate.setEnabled((num > 1) && scSupported);
1173             }
1174         }
1175 
1176         public void updateInfo() {
1177             Class cpCategory = Copies.class;
1178             Class csCategory = CopiesSupported.class;
1179             Class scCategory = SheetCollate.class;
1180             boolean cpSupported = false;
1181             scSupported = false;
1182 
1183             // setup Copies spinner
1184             if (psCurrent.isAttributeCategorySupported(cpCategory)) {
1185                 cpSupported = true;
1186             }
1187             CopiesSupported cs =
1188                 (CopiesSupported)psCurrent.getSupportedAttributeValues(
1189                                                        cpCategory, null, null);
1190             if (cs == null) {
1191                 cs = new CopiesSupported(1, 999);
1192             }
1193             Copies cp = (Copies)asCurrent.get(cpCategory);
1194             if (cp == null) {
1195                 cp = (Copies)psCurrent.getDefaultAttributeValue(cpCategory);
1196                 if (cp == null) {
1197                     cp = new Copies(1);
1198                 }
1199             }
1200             spinCopies.setEnabled(cpSupported);
1201             lblCopies.setEnabled(cpSupported);
1202 
1203             int[][] members = cs.getMembers();
1204             int min, max;
1205             if ((members.length > 0) && (members[0].length > 0)) {
1206                 min = members[0][0];
1207                 max = members[0][1];
1208             } else {
1209                 min = 1;
1210                 max = Integer.MAX_VALUE;
1211             }
1212             snModel.setMinimum(new Integer(min));
1213             snModel.setMaximum(new Integer(max));
1214 
1215             int value = cp.getValue();
1216             if ((value < min) || (value > max)) {
1217                 value = min;
1218             }
1219             snModel.setValue(new Integer(value));
1220 
1221             // setup Collate checkbox
1222             if (psCurrent.isAttributeCategorySupported(scCategory)) {
1223                 scSupported = true;
1224             }
1225             SheetCollate sc = (SheetCollate)asCurrent.get(scCategory);
1226             if (sc == null) {
1227                 sc = (SheetCollate)psCurrent.getDefaultAttributeValue(scCategory);
1228                 if (sc == null) {
1229                     sc = SheetCollate.UNCOLLATED;
1230                 }
1231             }
1232             cbCollate.setSelected(sc == SheetCollate.COLLATED);
1233             updateCollateCB();
1234         }
1235     }
1236 
1237 
1238 
1239 
1240     /**
1241      * The "Page Setup" tab.  Includes the controls for MediaSource/MediaTray,
1242      * OrientationRequested, and Sides.
1243      */
1244     private class PageSetupPanel extends JPanel {
1245 
1246         private MediaPanel pnlMedia;
1247         private OrientationPanel pnlOrientation;
1248         private MarginsPanel pnlMargins;
1249 
1250         public PageSetupPanel() {
1251             super();
1252 
1253             GridBagLayout gridbag = new GridBagLayout();
1254             GridBagConstraints c = new GridBagConstraints();
1255 
1256             setLayout(gridbag);
1257 
1258             c.fill = GridBagConstraints.BOTH;
1259             c.insets = panelInsets;
1260             c.weightx = 1.0;
1261             c.weighty = 1.0;
1262 
1263             c.gridwidth = GridBagConstraints.REMAINDER;
1264             pnlMedia = new MediaPanel();
1265             addToGB(pnlMedia, this, gridbag, c);
1266 
1267             pnlOrientation = new OrientationPanel();
1268             c.gridwidth = GridBagConstraints.RELATIVE;
1269             addToGB(pnlOrientation, this, gridbag, c);
1270 
1271             pnlMargins = new MarginsPanel();
1272             pnlOrientation.addOrientationListener(pnlMargins);
1273             pnlMedia.addMediaListener(pnlMargins);
1274             c.gridwidth = GridBagConstraints.REMAINDER;
1275             addToGB(pnlMargins, this, gridbag, c);
1276         }
1277 
1278         public void updateInfo() {
1279             pnlMedia.updateInfo();
1280             pnlOrientation.updateInfo();
1281             pnlMargins.updateInfo();
1282         }
1283     }
1284 
1285     private class MarginsPanel extends JPanel
1286                                implements ActionListener, FocusListener {
1287 
1288         private final String strTitle = getMsg("border.margins");
1289         private JFormattedTextField leftMargin, rightMargin,
1290                                     topMargin, bottomMargin;
1291         private JLabel lblLeft, lblRight, lblTop, lblBottom;
1292         private int units = MediaPrintableArea.MM;
1293         // storage for the last margin values calculated, -ve is uninitialised
1294         private float lmVal = -1f,rmVal = -1f, tmVal = -1f, bmVal = -1f;
1295         // storage for margins as objects mapped into orientation for display
1296         private Float lmObj,rmObj,tmObj,bmObj;
1297 
1298         public MarginsPanel() {
1299             super();
1300 
1301             GridBagLayout gridbag = new GridBagLayout();
1302             GridBagConstraints c = new GridBagConstraints();
1303             c.fill = GridBagConstraints.HORIZONTAL;
1304             c.weightx = 1.0;
1305             c.weighty = 0.0;
1306             c.insets = compInsets;
1307 
1308             setLayout(gridbag);
1309             setBorder(BorderFactory.createTitledBorder(strTitle));
1310 
1311             String unitsKey = "label.millimetres";
1312             String defaultCountry = Locale.getDefault().getCountry();
1313             if (defaultCountry != null &&
1314                 (defaultCountry.equals("") ||
1315                  defaultCountry.equals(Locale.US.getCountry()) ||
1316                  defaultCountry.equals(Locale.CANADA.getCountry()))) {
1317                 unitsKey = "label.inches";
1318                 units = MediaPrintableArea.INCH;
1319             }
1320             String unitsMsg = getMsg(unitsKey);
1321 
1322             DecimalFormat format;
1323             if (units == MediaPrintableArea.MM) {
1324                 format = new DecimalFormat("###.##");
1325                 format.setMaximumIntegerDigits(3);
1326             } else {
1327                 format = new DecimalFormat("##.##");
1328                 format.setMaximumIntegerDigits(2);
1329             }
1330 
1331             format.setMinimumFractionDigits(1);
1332             format.setMaximumFractionDigits(2);
1333             format.setMinimumIntegerDigits(1);
1334             format.setParseIntegerOnly(false);
1335             format.setDecimalSeparatorAlwaysShown(true);
1336             NumberFormatter nf = new NumberFormatter(format);
1337             nf.setMinimum(new Float(0.0f));
1338             nf.setMaximum(new Float(999.0f));
1339             nf.setAllowsInvalid(true);
1340             nf.setCommitsOnValidEdit(true);
1341 
1342             leftMargin = new JFormattedTextField(nf);
1343             leftMargin.addFocusListener(this);
1344             leftMargin.addActionListener(this);
1345             leftMargin.getAccessibleContext().setAccessibleName(
1346                                               getMsg("label.leftmargin"));
1347             rightMargin = new JFormattedTextField(nf);
1348             rightMargin.addFocusListener(this);
1349             rightMargin.addActionListener(this);
1350             rightMargin.getAccessibleContext().setAccessibleName(
1351                                               getMsg("label.rightmargin"));
1352             topMargin = new JFormattedTextField(nf);
1353             topMargin.addFocusListener(this);
1354             topMargin.addActionListener(this);
1355             topMargin.getAccessibleContext().setAccessibleName(
1356                                               getMsg("label.topmargin"));
1357             topMargin = new JFormattedTextField(nf);
1358             bottomMargin = new JFormattedTextField(nf);
1359             bottomMargin.addFocusListener(this);
1360             bottomMargin.addActionListener(this);
1361             bottomMargin.getAccessibleContext().setAccessibleName(
1362                                               getMsg("label.bottommargin"));
1363             topMargin = new JFormattedTextField(nf);
1364             c.gridwidth = GridBagConstraints.RELATIVE;
1365             lblLeft = new JLabel(getMsg("label.leftmargin") + " " + unitsMsg,
1366                                  JLabel.LEADING);
1367             lblLeft.setDisplayedMnemonic(getMnemonic("label.leftmargin"));
1368             lblLeft.setLabelFor(leftMargin);
1369             addToGB(lblLeft, this, gridbag, c);
1370 
1371             c.gridwidth = GridBagConstraints.REMAINDER;
1372             lblRight = new JLabel(getMsg("label.rightmargin") + " " + unitsMsg,
1373                                   JLabel.LEADING);
1374             lblRight.setDisplayedMnemonic(getMnemonic("label.rightmargin"));
1375             lblRight.setLabelFor(rightMargin);
1376             addToGB(lblRight, this, gridbag, c);
1377 
1378             c.gridwidth = GridBagConstraints.RELATIVE;
1379             addToGB(leftMargin, this, gridbag, c);
1380 
1381             c.gridwidth = GridBagConstraints.REMAINDER;
1382             addToGB(rightMargin, this, gridbag, c);
1383 
1384             // add an invisible spacing component.
1385             addToGB(new JPanel(), this, gridbag, c);
1386 
1387             c.gridwidth = GridBagConstraints.RELATIVE;
1388             lblTop = new JLabel(getMsg("label.topmargin") + " " + unitsMsg,
1389                                 JLabel.LEADING);
1390             lblTop.setDisplayedMnemonic(getMnemonic("label.topmargin"));
1391             lblTop.setLabelFor(topMargin);
1392             addToGB(lblTop, this, gridbag, c);
1393 
1394             c.gridwidth = GridBagConstraints.REMAINDER;
1395             lblBottom = new JLabel(getMsg("label.bottommargin") +
1396                                    " " + unitsMsg, JLabel.LEADING);
1397             lblBottom.setDisplayedMnemonic(getMnemonic("label.bottommargin"));
1398             lblBottom.setLabelFor(bottomMargin);
1399             addToGB(lblBottom, this, gridbag, c);
1400 
1401             c.gridwidth = GridBagConstraints.RELATIVE;
1402             addToGB(topMargin, this, gridbag, c);
1403 
1404             c.gridwidth = GridBagConstraints.REMAINDER;
1405             addToGB(bottomMargin, this, gridbag, c);
1406 
1407         }
1408 
1409         public void actionPerformed(ActionEvent e) {
1410             Object source = e.getSource();
1411             updateMargins(source);
1412         }
1413 
1414         public void focusLost(FocusEvent e) {
1415             Object source = e.getSource();
1416             updateMargins(source);
1417         }
1418 
1419         public void focusGained(FocusEvent e) {}
1420 
1421         /* Get the numbers, use to create a MPA.
1422          * If its valid, accept it and update the attribute set.
1423          * If its not valid, then reject it and call updateInfo()
1424          * to re-establish the previous entries.
1425          */
1426         public void updateMargins(Object source) {
1427             if (!(source instanceof JFormattedTextField)) {
1428                 return;
1429             } else {
1430                 JFormattedTextField tf = (JFormattedTextField)source;
1431                 Float val = (Float)tf.getValue();
1432                 if (val == null) {
1433                     return;
1434                 }
1435                 if (tf == leftMargin && val.equals(lmObj)) {
1436                     return;
1437                 }
1438                 if (tf == rightMargin && val.equals(rmObj)) {
1439                     return;
1440                 }
1441                 if (tf == topMargin && val.equals(tmObj)) {
1442                     return;
1443                 }
1444                 if (tf == bottomMargin && val.equals(bmObj)) {
1445                     return;
1446                 }
1447             }
1448 
1449             Float lmTmpObj = (Float)leftMargin.getValue();
1450             Float rmTmpObj = (Float)rightMargin.getValue();
1451             Float tmTmpObj = (Float)topMargin.getValue();
1452             Float bmTmpObj = (Float)bottomMargin.getValue();
1453 
1454             float lm = lmTmpObj.floatValue();
1455             float rm = rmTmpObj.floatValue();
1456             float tm = tmTmpObj.floatValue();
1457             float bm = bmTmpObj.floatValue();
1458 
1459             /* adjust for orientation */
1460             Class orCategory = OrientationRequested.class;
1461             OrientationRequested or =
1462                 (OrientationRequested)asCurrent.get(orCategory);
1463 
1464             if (or == null) {
1465                 or = (OrientationRequested)
1466                      psCurrent.getDefaultAttributeValue(orCategory);
1467             }
1468 
1469             float tmp;
1470             if (or == OrientationRequested.REVERSE_PORTRAIT) {
1471                 tmp = lm; lm = rm; rm = tmp;
1472                 tmp = tm; tm = bm; bm = tmp;
1473             } else if (or == OrientationRequested.LANDSCAPE) {
1474                 tmp = lm;
1475                 lm = tm;
1476                 tm = rm;
1477                 rm = bm;
1478                 bm = tmp;
1479             } else if (or == OrientationRequested.REVERSE_LANDSCAPE) {
1480                 tmp = lm;
1481                 lm = bm;
1482                 bm = rm;
1483                 rm = tm;
1484                 tm = tmp;
1485             }
1486             MediaPrintableArea mpa;
1487             if ((mpa = validateMargins(lm, rm, tm, bm)) != null) {
1488                 asCurrent.add(mpa);
1489                 lmVal = lm;
1490                 rmVal = rm;
1491                 tmVal = tm;
1492                 bmVal = bm;
1493                 lmObj = lmTmpObj;
1494                 rmObj = rmTmpObj;
1495                 tmObj = tmTmpObj;
1496                 bmObj = bmTmpObj;
1497             } else {
1498                 if (lmObj == null || rmObj == null ||
1499                     tmObj == null || rmObj == null) {
1500                     return;
1501                 } else {
1502                     leftMargin.setValue(lmObj);
1503                     rightMargin.setValue(rmObj);
1504                     topMargin.setValue(tmObj);
1505                     bottomMargin.setValue(bmObj);
1506 
1507                 }
1508             }
1509         }
1510 
1511         /*
1512          * This method either accepts the values and creates a new
1513          * MediaPrintableArea, or does nothing.
1514          * It should not attempt to create a printable area from anything
1515          * other than the exact values passed in.
1516          * But REMIND/TBD: it would be user friendly to replace margins the
1517          * user entered but are out of bounds with the minimum.
1518          * At that point this method will need to take responsibility for
1519          * updating the "stored" values and the UI.
1520          */
1521         private MediaPrintableArea validateMargins(float lm, float rm,
1522                                                    float tm, float bm) {
1523 
1524             Class mpaCategory = MediaPrintableArea.class;
1525             MediaPrintableArea mpa;
1526             MediaPrintableArea mpaMax = null;
1527             MediaSize mediaSize = null;
1528 
1529             Media media = (Media)asCurrent.get(Media.class);
1530             if (media == null || !(media instanceof MediaSizeName)) {
1531                 media = (Media)psCurrent.getDefaultAttributeValue(Media.class);
1532             }
1533             if (media != null && (media instanceof MediaSizeName)) {
1534                 MediaSizeName msn = (MediaSizeName)media;
1535                 mediaSize = MediaSize.getMediaSizeForName(msn);
1536             }
1537             if (mediaSize == null) {
1538                 mediaSize = new MediaSize(8.5f, 11f, Size2DSyntax.INCH);
1539             }
1540 
1541             if (media != null) {
1542                 PrintRequestAttributeSet tmpASet =
1543                     new HashPrintRequestAttributeSet(asCurrent);
1544                 tmpASet.add(media);
1545 
1546                 Object values =
1547                     psCurrent.getSupportedAttributeValues(mpaCategory,
1548                                                           docFlavor,
1549                                                           tmpASet);
1550                 if (values instanceof MediaPrintableArea[] &&
1551                     ((MediaPrintableArea[])values).length > 0) {
1552                     mpaMax = ((MediaPrintableArea[])values)[0];
1553 
1554                 }
1555             }
1556             if (mpaMax == null) {
1557                 mpaMax = new MediaPrintableArea(0f, 0f,
1558                                                 mediaSize.getX(units),
1559                                                 mediaSize.getY(units),
1560                                                 units);
1561             }
1562 
1563             float wid = mediaSize.getX(units);
1564             float hgt = mediaSize.getY(units);
1565             float pax = lm;
1566             float pay = tm;
1567             float paw = wid - lm - rm;
1568             float pah = hgt - tm - bm;
1569 
1570             if (paw <= 0f || pah <= 0f || pax < 0f || pay < 0f ||
1571                 pax < mpaMax.getX(units) || paw > mpaMax.getWidth(units) ||
1572                 pay < mpaMax.getY(units) || pah > mpaMax.getHeight(units)) {
1573                 return null;
1574             } else {
1575                 return new MediaPrintableArea(lm, tm, paw, pah, units);
1576             }
1577         }
1578 
1579         /* This is complex as a MediaPrintableArea is valid only within
1580          * a particular context of media size.
1581          * So we need a MediaSize as well as a MediaPrintableArea.
1582          * MediaSize can be obtained from MediaSizeName.
1583          * If the application specifies a MediaPrintableArea, we accept it
1584          * to the extent its valid for the Media they specify. If they
1585          * don't specify a Media, then the default is assumed.
1586          *
1587          * If an application doesn't define a MediaPrintableArea, we need to
1588          * create a suitable one, this is created using the specified (or
1589          * default) Media and default 1 inch margins. This is validated
1590          * against the paper in case this is too large for tiny media.
1591          */
1592         public void updateInfo() {
1593 
1594             if (isAWT) {
1595                 leftMargin.setEnabled(false);
1596                 rightMargin.setEnabled(false);
1597                 topMargin.setEnabled(false);
1598                 bottomMargin.setEnabled(false);
1599                 lblLeft.setEnabled(false);
1600                 lblRight.setEnabled(false);
1601                 lblTop.setEnabled(false);
1602                 lblBottom.setEnabled(false);
1603                 return;
1604             }
1605 
1606             Class mpaCategory = MediaPrintableArea.class;
1607             MediaPrintableArea mpa =
1608                  (MediaPrintableArea)asCurrent.get(mpaCategory);
1609             MediaPrintableArea mpaMax = null;
1610             MediaSize mediaSize = null;
1611 
1612             Media media = (Media)asCurrent.get(Media.class);
1613             if (media == null || !(media instanceof MediaSizeName)) {
1614                 media = (Media)psCurrent.getDefaultAttributeValue(Media.class);
1615             }
1616             if (media != null && (media instanceof MediaSizeName)) {
1617                 MediaSizeName msn = (MediaSizeName)media;
1618                 mediaSize = MediaSize.getMediaSizeForName(msn);
1619             }
1620             if (mediaSize == null) {
1621                 mediaSize = new MediaSize(8.5f, 11f, Size2DSyntax.INCH);
1622             }
1623 
1624             if (media != null) {
1625                 PrintRequestAttributeSet tmpASet =
1626                     new HashPrintRequestAttributeSet(asCurrent);
1627                 tmpASet.add(media);
1628 
1629                 Object values =
1630                     psCurrent.getSupportedAttributeValues(mpaCategory,
1631                                                           docFlavor,
1632                                                           tmpASet);
1633                 if (values instanceof MediaPrintableArea[] &&
1634                     ((MediaPrintableArea[])values).length > 0) {
1635                     mpaMax = ((MediaPrintableArea[])values)[0];
1636 
1637                 } else if (values instanceof MediaPrintableArea) {
1638                     mpaMax = (MediaPrintableArea)values;
1639                 }
1640             }
1641             if (mpaMax == null) {
1642                 mpaMax = new MediaPrintableArea(0f, 0f,
1643                                                 mediaSize.getX(units),
1644                                                 mediaSize.getY(units),
1645                                                 units);
1646             }
1647 
1648             /*
1649              * At this point we now know as best we can :-
1650              * - the media size
1651              * - the maximum corresponding printable area
1652              * - the media printable area specified by the client, if any.
1653              * The next step is to create a default MPA if none was specified.
1654              * 1" margins are used unless they are disproportionately
1655              * large compared to the size of the media.
1656              */
1657 
1658             float wid = mediaSize.getX(MediaPrintableArea.INCH);
1659             float hgt = mediaSize.getY(MediaPrintableArea.INCH);
1660             float maxMarginRatio = 5f;
1661             float xMgn, yMgn;
1662             if (wid > maxMarginRatio) {
1663                 xMgn = 1f;
1664             } else {
1665                 xMgn = wid / maxMarginRatio;
1666             }
1667             if (hgt > maxMarginRatio) {
1668                 yMgn = 1f;
1669             } else {
1670                 yMgn = hgt / maxMarginRatio;
1671             }
1672 
1673             if (mpa == null) {
1674                 mpa = new MediaPrintableArea(xMgn, yMgn,
1675                                              wid-(2*xMgn), hgt-(2*yMgn),
1676                                              MediaPrintableArea.INCH);
1677                 asCurrent.add(mpa);
1678             }
1679             float pax = mpa.getX(units);
1680             float pay = mpa.getY(units);
1681             float paw = mpa.getWidth(units);
1682             float pah = mpa.getHeight(units);
1683             float paxMax = mpaMax.getX(units);
1684             float payMax = mpaMax.getY(units);
1685             float pawMax = mpaMax.getWidth(units);
1686             float pahMax = mpaMax.getHeight(units);
1687 
1688 
1689             boolean invalid = false;
1690 
1691             // If the paper is set to something which is too small to
1692             // accommodate a specified printable area, perhaps carried
1693             // over from a larger paper, the adjustment that needs to be
1694             // performed should seem the most natural from a user's viewpoint.
1695             // Since the user is specifying margins, then we are biased
1696             // towards keeping the margins as close to what is specified as
1697             // possible, shrinking or growing the printable area.
1698             // But the API uses printable area, so you need to know the
1699             // media size in which the margins were previously interpreted,
1700             // or at least have a record of the margins.
1701             // In the case that this is the creation of this UI we do not
1702             // have this record, so we are somewhat reliant on the client
1703             // to supply a reasonable default
1704             wid = mediaSize.getX(units);
1705             hgt = mediaSize.getY(units);
1706             if (lmVal >= 0f) {
1707                 invalid = true;
1708 
1709                 if (lmVal + rmVal > wid) {
1710                     // margins impossible, but maintain P.A if can
1711                     if (paw > pawMax) {
1712                         paw = pawMax;
1713                     }
1714                     // try to centre the printable area.
1715                     pax = (wid - paw)/2f;
1716                 } else {
1717                     pax = (lmVal >= paxMax) ? lmVal : paxMax;
1718                     paw = wid - pax - rmVal;
1719                 }
1720                 if (tmVal + bmVal > hgt) {
1721                     if (pah > pahMax) {
1722                         pah = pahMax;
1723                     }
1724                     pay = (hgt - pah)/2f;
1725                 } else {
1726                     pay = (tmVal >= payMax) ? tmVal : payMax;
1727                     pah = hgt - pay - bmVal;
1728                 }
1729             }
1730             if (pax < paxMax) {
1731                 invalid = true;
1732                 pax = paxMax;
1733             }
1734             if (pay < payMax) {
1735                 invalid = true;
1736                 pay = payMax;
1737             }
1738             if (paw > pawMax) {
1739                 invalid = true;
1740                 paw = pawMax;
1741             }
1742             if (pah > pahMax) {
1743                 invalid = true;
1744                 pah = pahMax;
1745             }
1746 
1747             if ((pax + paw > paxMax + pawMax) || (paw <= 0f)) {
1748                 invalid = true;
1749                 pax = paxMax;
1750                 paw = pawMax;
1751             }
1752             if ((pay + pah > payMax + pahMax) || (pah <= 0f)) {
1753                 invalid = true;
1754                 pay = payMax;
1755                 pah = pahMax;
1756             }
1757 
1758             if (invalid) {
1759                 mpa = new MediaPrintableArea(pax, pay, paw, pah, units);
1760                 asCurrent.add(mpa);
1761             }
1762 
1763             /* We now have a valid printable area.
1764              * Turn it into margins, using the mediaSize
1765              */
1766             lmVal = pax;
1767             tmVal = pay;
1768             rmVal = mediaSize.getX(units) - pax - paw;
1769             bmVal = mediaSize.getY(units) - pay - pah;
1770 
1771             lmObj = new Float(lmVal);
1772             rmObj = new Float(rmVal);
1773             tmObj = new Float(tmVal);
1774             bmObj = new Float(bmVal);
1775 
1776             /* Now we know the values to use, we need to assign them
1777              * to the fields appropriate for the orientation.
1778              * Note: if orientation changes this method must be called.
1779              */
1780             Class orCategory = OrientationRequested.class;
1781             OrientationRequested or =
1782                 (OrientationRequested)asCurrent.get(orCategory);
1783 
1784             if (or == null) {
1785                 or = (OrientationRequested)
1786                      psCurrent.getDefaultAttributeValue(orCategory);
1787             }
1788 
1789             Float tmp;
1790 
1791             if (or == OrientationRequested.REVERSE_PORTRAIT) {
1792                 tmp = lmObj; lmObj = rmObj; rmObj = tmp;
1793                 tmp = tmObj; tmObj = bmObj; bmObj = tmp;
1794             }  else if (or == OrientationRequested.LANDSCAPE) {
1795                 tmp = lmObj;
1796                 lmObj = bmObj;
1797                 bmObj = rmObj;
1798                 rmObj = tmObj;
1799                 tmObj = tmp;
1800             }  else if (or == OrientationRequested.REVERSE_LANDSCAPE) {
1801                 tmp = lmObj;
1802                 lmObj = tmObj;
1803                 tmObj = rmObj;
1804                 rmObj = bmObj;
1805                 bmObj = tmp;
1806             }
1807 
1808             leftMargin.setValue(lmObj);
1809             rightMargin.setValue(rmObj);
1810             topMargin.setValue(tmObj);
1811             bottomMargin.setValue(bmObj);
1812         }
1813     }
1814 
1815     private class MediaPanel extends JPanel implements ItemListener {
1816 
1817         private final String strTitle = getMsg("border.media");
1818         private JLabel lblSize, lblSource;
1819         private JComboBox cbSize, cbSource;
1820         private Vector sizes = new Vector();
1821         private Vector sources = new Vector();
1822         private MarginsPanel pnlMargins = null;
1823 
1824         public MediaPanel() {
1825             super();
1826 
1827             GridBagLayout gridbag = new GridBagLayout();
1828             GridBagConstraints c = new GridBagConstraints();
1829 
1830             setLayout(gridbag);
1831             setBorder(BorderFactory.createTitledBorder(strTitle));
1832 
1833             cbSize = new JComboBox();
1834             cbSource = new JComboBox();
1835 
1836             c.fill = GridBagConstraints.BOTH;
1837             c.insets = compInsets;
1838             c.weighty = 1.0;
1839 
1840             c.weightx = 0.0;
1841             lblSize = new JLabel(getMsg("label.size"), JLabel.TRAILING);
1842             lblSize.setDisplayedMnemonic(getMnemonic("label.size"));
1843             lblSize.setLabelFor(cbSize);
1844             addToGB(lblSize, this, gridbag, c);
1845             c.weightx = 1.0;
1846             c.gridwidth = GridBagConstraints.REMAINDER;
1847             addToGB(cbSize, this, gridbag, c);
1848 
1849             c.weightx = 0.0;
1850             c.gridwidth = 1;
1851             lblSource = new JLabel(getMsg("label.source"), JLabel.TRAILING);
1852             lblSource.setDisplayedMnemonic(getMnemonic("label.source"));
1853             lblSource.setLabelFor(cbSource);
1854             addToGB(lblSource, this, gridbag, c);
1855             c.gridwidth = GridBagConstraints.REMAINDER;
1856             addToGB(cbSource, this, gridbag, c);
1857         }
1858 
1859         private String getMediaName(String key) {
1860             try {
1861                 // replace characters that would be invalid in
1862                 // a resource key with valid characters
1863                 String newkey = key.replace(' ', '-');
1864                 newkey = newkey.replace('#', 'n');
1865 
1866                 return messageRB.getString(newkey);
1867             } catch (java.util.MissingResourceException e) {
1868                 return key;
1869             }
1870         }
1871 
1872         public void itemStateChanged(ItemEvent e) {
1873             Object source = e.getSource();
1874 
1875             if (e.getStateChange() == ItemEvent.SELECTED) {
1876                 if (source == cbSize) {
1877                     int index = cbSize.getSelectedIndex();
1878 
1879                     if ((index >= 0) && (index < sizes.size())) {
1880                         if ((cbSource.getItemCount() > 1) &&
1881                             (cbSource.getSelectedIndex() >= 1))
1882                         {
1883                             int src = cbSource.getSelectedIndex() - 1;
1884                             MediaTray mt = (MediaTray)sources.get(src);
1885                             asCurrent.add(new SunAlternateMedia(mt));
1886                         }
1887                         asCurrent.add((MediaSizeName)sizes.get(index));
1888                     }
1889                 } else if (source == cbSource) {
1890                     int index = cbSource.getSelectedIndex();
1891 
1892                     if ((index >= 1) && (index < (sources.size() + 1))) {
1893                        asCurrent.remove(SunAlternateMedia.class);
1894                        MediaTray newTray = (MediaTray)sources.get(index - 1);
1895                        Media m = (Media)asCurrent.get(Media.class);
1896                        if (m == null || m instanceof MediaTray) {
1897                            asCurrent.add(newTray);
1898                        } else if (m instanceof MediaSizeName) {
1899                            MediaSizeName msn = (MediaSizeName)m;
1900                            Media def = (Media)psCurrent.getDefaultAttributeValue(Media.class);
1901                            if (def instanceof MediaSizeName && def.equals(msn)) {
1902                                asCurrent.add(newTray);
1903                            } else {
1904                                /* Non-default paper size, so need to store tray
1905                                 * as SunAlternateMedia
1906                                 */
1907                                asCurrent.add(new SunAlternateMedia(newTray));
1908                            }
1909                        }
1910                     } else if (index == 0) {
1911                         asCurrent.remove(SunAlternateMedia.class);
1912                         if (cbSize.getItemCount() > 0) {
1913                             int size = cbSize.getSelectedIndex();
1914                             asCurrent.add((MediaSizeName)sizes.get(size));
1915                         }
1916                     }
1917                 }
1918             // orientation affects display of margins.
1919                 if (pnlMargins != null) {
1920                     pnlMargins.updateInfo();
1921                 }
1922             }
1923         }
1924 
1925 
1926         /* this is ad hoc to keep things simple */
1927         public void addMediaListener(MarginsPanel pnl) {
1928             pnlMargins = pnl;
1929         }
1930         public void updateInfo() {
1931             Class mdCategory = Media.class;
1932             Class amCategory = SunAlternateMedia.class;
1933             boolean mediaSupported = false;
1934 
1935             cbSize.removeItemListener(this);
1936             cbSize.removeAllItems();
1937             cbSource.removeItemListener(this);
1938             cbSource.removeAllItems();
1939             cbSource.addItem(getMediaName("auto-select"));
1940 
1941             sizes.clear();
1942             sources.clear();
1943 
1944             if (psCurrent.isAttributeCategorySupported(mdCategory)) {
1945                 mediaSupported = true;
1946 
1947                 Object values =
1948                     psCurrent.getSupportedAttributeValues(mdCategory,
1949                                                           docFlavor,
1950                                                           asCurrent);
1951 
1952                 if (values instanceof Media[]) {
1953                     Media[] media = (Media[])values;
1954 
1955                     for (int i = 0; i < media.length; i++) {
1956                         Media medium = media[i];
1957 
1958                         if (medium instanceof MediaSizeName) {
1959                             sizes.add(medium);
1960                             cbSize.addItem(getMediaName(medium.toString()));
1961                         } else if (medium instanceof MediaTray) {
1962                             sources.add(medium);
1963                             cbSource.addItem(getMediaName(medium.toString()));
1964                         }
1965                     }
1966                 }
1967             }
1968 
1969             boolean msSupported = (mediaSupported && (sizes.size() > 0));
1970             lblSize.setEnabled(msSupported);
1971             cbSize.setEnabled(msSupported);
1972 
1973             if (isAWT) {
1974                 cbSource.setEnabled(false);
1975                 lblSource.setEnabled(false);
1976             } else {
1977                 cbSource.setEnabled(mediaSupported);
1978             }
1979 
1980             if (mediaSupported) {
1981 
1982                 Media medium = (Media)asCurrent.get(mdCategory);
1983 
1984                // initialize size selection to default
1985                 Media defMedia = (Media)psCurrent.getDefaultAttributeValue(mdCategory);
1986                 if (defMedia instanceof MediaSizeName) {
1987                     cbSize.setSelectedIndex(sizes.size() > 0 ? sizes.indexOf(defMedia) : -1);
1988                 }
1989 
1990                 if (medium == null ||
1991                     !psCurrent.isAttributeValueSupported(medium,
1992                                                          docFlavor, asCurrent)) {
1993 
1994                     medium = defMedia;
1995 
1996                     if (medium == null) {
1997                         if (sizes.size() > 0) {
1998                             medium = (Media)sizes.get(0);
1999                         }
2000                     }
2001                     if (medium != null) {
2002                         asCurrent.add(medium);
2003                     }
2004                 }
2005                 if (medium != null) {
2006                     if (medium instanceof MediaSizeName) {
2007                         MediaSizeName ms = (MediaSizeName)medium;
2008                         cbSize.setSelectedIndex(sizes.indexOf(ms));
2009                     } else if (medium instanceof MediaTray) {
2010                         MediaTray mt = (MediaTray)medium;
2011                         cbSource.setSelectedIndex(sources.indexOf(mt) + 1);
2012                     }
2013                 } else {
2014                     cbSize.setSelectedIndex(sizes.size() > 0 ? 0 : -1);
2015                     cbSource.setSelectedIndex(0);
2016                 }
2017 
2018                 SunAlternateMedia alt = (SunAlternateMedia)asCurrent.get(amCategory);
2019                 if (alt != null) {
2020                     Media md = alt.getMedia();
2021                     if (md instanceof MediaTray) {
2022                         MediaTray mt = (MediaTray)md;
2023                         cbSource.setSelectedIndex(sources.indexOf(mt) + 1);
2024                     }
2025                 }
2026 
2027                 int selIndex = cbSize.getSelectedIndex();
2028                 if ((selIndex >= 0) && (selIndex < sizes.size())) {
2029                   asCurrent.add((MediaSizeName)sizes.get(selIndex));
2030                 }
2031 
2032                 selIndex = cbSource.getSelectedIndex();
2033                 if ((selIndex >= 1) && (selIndex < (sources.size()+1))) {
2034                     MediaTray mt = (MediaTray)sources.get(selIndex-1);
2035                     if (medium instanceof MediaTray) {
2036                         asCurrent.add(mt);
2037                     } else {
2038                         asCurrent.add(new SunAlternateMedia(mt));
2039                     }
2040                 }
2041 
2042 
2043             }
2044             cbSize.addItemListener(this);
2045             cbSource.addItemListener(this);
2046         }
2047     }
2048 
2049     private class OrientationPanel extends JPanel
2050         implements ActionListener
2051     {
2052         private final String strTitle = getMsg("border.orientation");
2053         private IconRadioButton rbPortrait, rbLandscape,
2054                                 rbRevPortrait, rbRevLandscape;
2055         private MarginsPanel pnlMargins = null;
2056 
2057         public OrientationPanel() {
2058             super();
2059 
2060             GridBagLayout gridbag = new GridBagLayout();
2061             GridBagConstraints c = new GridBagConstraints();
2062 
2063             setLayout(gridbag);
2064             setBorder(BorderFactory.createTitledBorder(strTitle));
2065 
2066             c.fill = GridBagConstraints.BOTH;
2067             c.insets = compInsets;
2068             c.weighty = 1.0;
2069             c.gridwidth = GridBagConstraints.REMAINDER;
2070 
2071             ButtonGroup bg = new ButtonGroup();
2072             rbPortrait = new IconRadioButton("radiobutton.portrait",
2073                                              "orientPortrait.png", true,
2074                                              bg, this);
2075             rbPortrait.addActionListener(this);
2076             addToGB(rbPortrait, this, gridbag, c);
2077             rbLandscape = new IconRadioButton("radiobutton.landscape",
2078                                               "orientLandscape.png", false,
2079                                               bg, this);
2080             rbLandscape.addActionListener(this);
2081             addToGB(rbLandscape, this, gridbag, c);
2082             rbRevPortrait = new IconRadioButton("radiobutton.revportrait",
2083                                                 "orientRevPortrait.png", false,
2084                                                 bg, this);
2085             rbRevPortrait.addActionListener(this);
2086             addToGB(rbRevPortrait, this, gridbag, c);
2087             rbRevLandscape = new IconRadioButton("radiobutton.revlandscape",
2088                                                  "orientRevLandscape.png", false,
2089                                                  bg, this);
2090             rbRevLandscape.addActionListener(this);
2091             addToGB(rbRevLandscape, this, gridbag, c);
2092         }
2093 
2094         public void actionPerformed(ActionEvent e) {
2095             Object source = e.getSource();
2096 
2097             if (rbPortrait.isSameAs(source)) {
2098                 asCurrent.add(OrientationRequested.PORTRAIT);
2099             } else if (rbLandscape.isSameAs(source)) {
2100                 asCurrent.add(OrientationRequested.LANDSCAPE);
2101             } else if (rbRevPortrait.isSameAs(source)) {
2102                 asCurrent.add(OrientationRequested.REVERSE_PORTRAIT);
2103             } else if (rbRevLandscape.isSameAs(source)) {
2104                 asCurrent.add(OrientationRequested.REVERSE_LANDSCAPE);
2105             }
2106             // orientation affects display of margins.
2107             if (pnlMargins != null) {
2108                 pnlMargins.updateInfo();
2109             }
2110         }
2111 
2112         /* This is ad hoc to keep things simple */
2113         void addOrientationListener(MarginsPanel pnl) {
2114             pnlMargins = pnl;
2115         }
2116 
2117         public void updateInfo() {
2118             Class orCategory = OrientationRequested.class;
2119             boolean pSupported = false;
2120             boolean lSupported = false;
2121             boolean rpSupported = false;
2122             boolean rlSupported = false;
2123 
2124             if (isAWT) {
2125                 pSupported = true;
2126                 lSupported = true;
2127             } else
2128             if (psCurrent.isAttributeCategorySupported(orCategory)) {
2129                 Object values =
2130                     psCurrent.getSupportedAttributeValues(orCategory,
2131                                                           docFlavor,
2132                                                           asCurrent);
2133 
2134                 if (values instanceof OrientationRequested[]) {
2135                     OrientationRequested[] ovalues =
2136                         (OrientationRequested[])values;
2137 
2138                     for (int i = 0; i < ovalues.length; i++) {
2139                         OrientationRequested value = ovalues[i];
2140 
2141                         if (value == OrientationRequested.PORTRAIT) {
2142                             pSupported = true;
2143                         } else if (value == OrientationRequested.LANDSCAPE) {
2144                             lSupported = true;
2145                         } else if (value == OrientationRequested.REVERSE_PORTRAIT) {
2146                             rpSupported = true;
2147                         } else if (value == OrientationRequested.REVERSE_LANDSCAPE) {
2148                             rlSupported = true;
2149                         }
2150                     }
2151                 }
2152             }
2153 
2154 
2155             rbPortrait.setEnabled(pSupported);
2156             rbLandscape.setEnabled(lSupported);
2157             rbRevPortrait.setEnabled(rpSupported);
2158             rbRevLandscape.setEnabled(rlSupported);
2159 
2160             OrientationRequested or = (OrientationRequested)asCurrent.get(orCategory);
2161             if (or == null ||
2162                 !psCurrent.isAttributeValueSupported(or, docFlavor, asCurrent)) {
2163 
2164                 or = (OrientationRequested)psCurrent.getDefaultAttributeValue(orCategory);
2165                 // need to validate if default is not supported
2166                 if ((or != null) &&
2167                    !psCurrent.isAttributeValueSupported(or, docFlavor, asCurrent)) {
2168                     or = null;
2169                     Object values =
2170                         psCurrent.getSupportedAttributeValues(orCategory,
2171                                                               docFlavor,
2172                                                               asCurrent);
2173                     if (values instanceof OrientationRequested[]) {
2174                         OrientationRequested[] orValues =
2175                                             (OrientationRequested[])values;
2176                         if (orValues.length > 1) {
2177                             // get the first in the list
2178                             or = orValues[0];
2179                         }
2180                     }
2181                 }
2182 
2183                 if (or == null) {
2184                     or = OrientationRequested.PORTRAIT;
2185                 }
2186                 asCurrent.add(or);
2187             }
2188 
2189             if (or == OrientationRequested.PORTRAIT) {
2190                 rbPortrait.setSelected(true);
2191             } else if (or == OrientationRequested.LANDSCAPE) {
2192                 rbLandscape.setSelected(true);
2193             } else if (or == OrientationRequested.REVERSE_PORTRAIT) {
2194                 rbRevPortrait.setSelected(true);
2195             } else { // if (or == OrientationRequested.REVERSE_LANDSCAPE)
2196                 rbRevLandscape.setSelected(true);
2197             }
2198         }
2199     }
2200 
2201 
2202 
2203     /**
2204      * The "Appearance" tab.  Includes the controls for Chromaticity,
2205      * PrintQuality, JobPriority, JobName, and other related job attributes.
2206      */
2207     private class AppearancePanel extends JPanel {
2208 
2209         private ChromaticityPanel pnlChromaticity;
2210         private QualityPanel pnlQuality;
2211         private JobAttributesPanel pnlJobAttributes;
2212         private SidesPanel pnlSides;
2213 
2214         public AppearancePanel() {
2215             super();
2216 
2217             GridBagLayout gridbag = new GridBagLayout();
2218             GridBagConstraints c = new GridBagConstraints();
2219 
2220             setLayout(gridbag);
2221 
2222             c.fill = GridBagConstraints.BOTH;
2223             c.insets = panelInsets;
2224             c.weightx = 1.0;
2225             c.weighty = 1.0;
2226 
2227             c.gridwidth = GridBagConstraints.RELATIVE;
2228             pnlChromaticity = new ChromaticityPanel();
2229             addToGB(pnlChromaticity, this, gridbag, c);
2230 
2231             c.gridwidth = GridBagConstraints.REMAINDER;
2232             pnlQuality = new QualityPanel();
2233             addToGB(pnlQuality, this, gridbag, c);
2234 
2235             c.gridwidth = 1;
2236             pnlSides = new SidesPanel();
2237             addToGB(pnlSides, this, gridbag, c);
2238 
2239             c.gridwidth = GridBagConstraints.REMAINDER;
2240             pnlJobAttributes = new JobAttributesPanel();
2241             addToGB(pnlJobAttributes, this, gridbag, c);
2242 
2243         }
2244 
2245         public void updateInfo() {
2246             pnlChromaticity.updateInfo();
2247             pnlQuality.updateInfo();
2248             pnlSides.updateInfo();
2249             pnlJobAttributes.updateInfo();
2250         }
2251     }
2252 
2253     private class ChromaticityPanel extends JPanel
2254         implements ActionListener
2255     {
2256         private final String strTitle = getMsg("border.chromaticity");
2257         private JRadioButton rbMonochrome, rbColor;
2258 
2259         public ChromaticityPanel() {
2260             super();
2261 
2262             GridBagLayout gridbag = new GridBagLayout();
2263             GridBagConstraints c = new GridBagConstraints();
2264 
2265             setLayout(gridbag);
2266             setBorder(BorderFactory.createTitledBorder(strTitle));
2267 
2268             c.fill = GridBagConstraints.BOTH;
2269             c.gridwidth = GridBagConstraints.REMAINDER;
2270             c.weighty = 1.0;
2271 
2272             ButtonGroup bg = new ButtonGroup();
2273             rbMonochrome = createRadioButton("radiobutton.monochrome", this);
2274             rbMonochrome.setSelected(true);
2275             bg.add(rbMonochrome);
2276             addToGB(rbMonochrome, this, gridbag, c);
2277             rbColor = createRadioButton("radiobutton.color", this);
2278             bg.add(rbColor);
2279             addToGB(rbColor, this, gridbag, c);
2280         }
2281 
2282         public void actionPerformed(ActionEvent e) {
2283             Object source = e.getSource();
2284 
2285             // REMIND: use isSameAs if we move to a IconRB in the future
2286             if (source == rbMonochrome) {
2287                 asCurrent.add(Chromaticity.MONOCHROME);
2288             } else if (source == rbColor) {
2289                 asCurrent.add(Chromaticity.COLOR);
2290             }
2291         }
2292 
2293         public void updateInfo() {
2294             Class chCategory = Chromaticity.class;
2295             boolean monoSupported = false;
2296             boolean colorSupported = false;
2297 
2298             if (isAWT) {
2299                 monoSupported = true;
2300                 colorSupported = true;
2301             } else
2302             if (psCurrent.isAttributeCategorySupported(chCategory)) {
2303                 Object values =
2304                     psCurrent.getSupportedAttributeValues(chCategory,
2305                                                           docFlavor,
2306                                                           asCurrent);
2307 
2308                 if (values instanceof Chromaticity[]) {
2309                     Chromaticity[] cvalues = (Chromaticity[])values;
2310 
2311                     for (int i = 0; i < cvalues.length; i++) {
2312                         Chromaticity value = cvalues[i];
2313 
2314                         if (value == Chromaticity.MONOCHROME) {
2315                             monoSupported = true;
2316                         } else if (value == Chromaticity.COLOR) {
2317                             colorSupported = true;
2318                         }
2319                     }
2320                 }
2321             }
2322 
2323 
2324             rbMonochrome.setEnabled(monoSupported);
2325             rbColor.setEnabled(colorSupported);
2326 
2327             Chromaticity ch = (Chromaticity)asCurrent.get(chCategory);
2328             if (ch == null) {
2329                 ch = (Chromaticity)psCurrent.getDefaultAttributeValue(chCategory);
2330                 if (ch == null) {
2331                     ch = Chromaticity.MONOCHROME;
2332                 }
2333             }
2334 
2335             if (ch == Chromaticity.MONOCHROME) {
2336                 rbMonochrome.setSelected(true);
2337             } else { // if (ch == Chromaticity.COLOR)
2338                 rbColor.setSelected(true);
2339             }
2340         }
2341     }
2342 
2343     private class QualityPanel extends JPanel
2344         implements ActionListener
2345     {
2346         private final String strTitle = getMsg("border.quality");
2347         private JRadioButton rbDraft, rbNormal, rbHigh;
2348 
2349         public QualityPanel() {
2350             super();
2351 
2352             GridBagLayout gridbag = new GridBagLayout();
2353             GridBagConstraints c = new GridBagConstraints();
2354 
2355             setLayout(gridbag);
2356             setBorder(BorderFactory.createTitledBorder(strTitle));
2357 
2358             c.fill = GridBagConstraints.BOTH;
2359             c.gridwidth = GridBagConstraints.REMAINDER;
2360             c.weighty = 1.0;
2361 
2362             ButtonGroup bg = new ButtonGroup();
2363             rbDraft = createRadioButton("radiobutton.draftq", this);
2364             bg.add(rbDraft);
2365             addToGB(rbDraft, this, gridbag, c);
2366             rbNormal = createRadioButton("radiobutton.normalq", this);
2367             rbNormal.setSelected(true);
2368             bg.add(rbNormal);
2369             addToGB(rbNormal, this, gridbag, c);
2370             rbHigh = createRadioButton("radiobutton.highq", this);
2371             bg.add(rbHigh);
2372             addToGB(rbHigh, this, gridbag, c);
2373         }
2374 
2375         public void actionPerformed(ActionEvent e) {
2376             Object source = e.getSource();
2377 
2378             if (source == rbDraft) {
2379                 asCurrent.add(PrintQuality.DRAFT);
2380             } else if (source == rbNormal) {
2381                 asCurrent.add(PrintQuality.NORMAL);
2382             } else if (source == rbHigh) {
2383                 asCurrent.add(PrintQuality.HIGH);
2384             }
2385         }
2386 
2387         public void updateInfo() {
2388             Class pqCategory = PrintQuality.class;
2389             boolean draftSupported = false;
2390             boolean normalSupported = false;
2391             boolean highSupported = false;
2392 
2393             if (isAWT) {
2394                 draftSupported = true;
2395                 normalSupported = true;
2396                 highSupported = true;
2397             } else
2398             if (psCurrent.isAttributeCategorySupported(pqCategory)) {
2399                 Object values =
2400                     psCurrent.getSupportedAttributeValues(pqCategory,
2401                                                           docFlavor,
2402                                                           asCurrent);
2403 
2404                 if (values instanceof PrintQuality[]) {
2405                     PrintQuality[] qvalues = (PrintQuality[])values;
2406 
2407                     for (int i = 0; i < qvalues.length; i++) {
2408                         PrintQuality value = qvalues[i];
2409 
2410                         if (value == PrintQuality.DRAFT) {
2411                             draftSupported = true;
2412                         } else if (value == PrintQuality.NORMAL) {
2413                             normalSupported = true;
2414                         } else if (value == PrintQuality.HIGH) {
2415                             highSupported = true;
2416                         }
2417                     }
2418                 }
2419             }
2420 
2421             rbDraft.setEnabled(draftSupported);
2422             rbNormal.setEnabled(normalSupported);
2423             rbHigh.setEnabled(highSupported);
2424 
2425             PrintQuality pq = (PrintQuality)asCurrent.get(pqCategory);
2426             if (pq == null) {
2427                 pq = (PrintQuality)psCurrent.getDefaultAttributeValue(pqCategory);
2428                 if (pq == null) {
2429                     pq = PrintQuality.NORMAL;
2430                 }
2431             }
2432 
2433             if (pq == PrintQuality.DRAFT) {
2434                 rbDraft.setSelected(true);
2435             } else if (pq == PrintQuality.NORMAL) {
2436                 rbNormal.setSelected(true);
2437             } else { // if (pq == PrintQuality.HIGH)
2438                 rbHigh.setSelected(true);
2439             }
2440         }
2441 
2442 
2443     }
2444     private class SidesPanel extends JPanel
2445         implements ActionListener
2446     {
2447         private final String strTitle = getMsg("border.sides");
2448         private IconRadioButton rbOneSide, rbTumble, rbDuplex;
2449 
2450         public SidesPanel() {
2451             super();
2452 
2453             GridBagLayout gridbag = new GridBagLayout();
2454             GridBagConstraints c = new GridBagConstraints();
2455 
2456             setLayout(gridbag);
2457             setBorder(BorderFactory.createTitledBorder(strTitle));
2458 
2459             c.fill = GridBagConstraints.BOTH;
2460             c.insets = compInsets;
2461             c.weighty = 1.0;
2462             c.gridwidth = GridBagConstraints.REMAINDER;
2463 
2464             ButtonGroup bg = new ButtonGroup();
2465             rbOneSide = new IconRadioButton("radiobutton.oneside",
2466                                             "oneside.png", true,
2467                                             bg, this);
2468             rbOneSide.addActionListener(this);
2469             addToGB(rbOneSide, this, gridbag, c);
2470             rbTumble = new IconRadioButton("radiobutton.tumble",
2471                                            "tumble.png", false,
2472                                            bg, this);
2473             rbTumble.addActionListener(this);
2474             addToGB(rbTumble, this, gridbag, c);
2475             rbDuplex = new IconRadioButton("radiobutton.duplex",
2476                                            "duplex.png", false,
2477                                            bg, this);
2478             rbDuplex.addActionListener(this);
2479             c.gridwidth = GridBagConstraints.REMAINDER;
2480             addToGB(rbDuplex, this, gridbag, c);
2481         }
2482 
2483         public void actionPerformed(ActionEvent e) {
2484             Object source = e.getSource();
2485 
2486             if (rbOneSide.isSameAs(source)) {
2487                 asCurrent.add(Sides.ONE_SIDED);
2488             } else if (rbTumble.isSameAs(source)) {
2489                 asCurrent.add(Sides.TUMBLE);
2490             } else if (rbDuplex.isSameAs(source)) {
2491                 asCurrent.add(Sides.DUPLEX);
2492             }
2493         }
2494 
2495         public void updateInfo() {
2496             Class sdCategory = Sides.class;
2497             boolean osSupported = false;
2498             boolean tSupported = false;
2499             boolean dSupported = false;
2500 
2501             if (psCurrent.isAttributeCategorySupported(sdCategory)) {
2502                 Object values =
2503                     psCurrent.getSupportedAttributeValues(sdCategory,
2504                                                           docFlavor,
2505                                                           asCurrent);
2506 
2507                 if (values instanceof Sides[]) {
2508                     Sides[] svalues = (Sides[])values;
2509 
2510                     for (int i = 0; i < svalues.length; i++) {
2511                         Sides value = svalues[i];
2512 
2513                         if (value == Sides.ONE_SIDED) {
2514                             osSupported = true;
2515                         } else if (value == Sides.TUMBLE) {
2516                             tSupported = true;
2517                         } else if (value == Sides.DUPLEX) {
2518                             dSupported = true;
2519                         }
2520                     }
2521                 }
2522             }
2523             rbOneSide.setEnabled(osSupported);
2524             rbTumble.setEnabled(tSupported);
2525             rbDuplex.setEnabled(dSupported);
2526 
2527             Sides sd = (Sides)asCurrent.get(sdCategory);
2528             if (sd == null) {
2529                 sd = (Sides)psCurrent.getDefaultAttributeValue(sdCategory);
2530                 if (sd == null) {
2531                     sd = Sides.ONE_SIDED;
2532                 }
2533             }
2534 
2535             if (sd == Sides.ONE_SIDED) {
2536                 rbOneSide.setSelected(true);
2537             } else if (sd == Sides.TUMBLE) {
2538                 rbTumble.setSelected(true);
2539             } else { // if (sd == Sides.DUPLEX)
2540                 rbDuplex.setSelected(true);
2541             }
2542         }
2543     }
2544 
2545 
2546 
2547     private class JobAttributesPanel extends JPanel
2548         implements ActionListener, ChangeListener, FocusListener
2549     {
2550         private final String strTitle = getMsg("border.jobattributes");
2551         private JLabel lblPriority, lblJobName, lblUserName;
2552         private JSpinner spinPriority;
2553         private SpinnerNumberModel snModel;
2554         private JCheckBox cbJobSheets;
2555         private JTextField tfJobName, tfUserName;
2556 
2557         public JobAttributesPanel() {
2558             super();
2559 
2560             GridBagLayout gridbag = new GridBagLayout();
2561             GridBagConstraints c = new GridBagConstraints();
2562 
2563             setLayout(gridbag);
2564             setBorder(BorderFactory.createTitledBorder(strTitle));
2565 
2566             c.fill = GridBagConstraints.NONE;
2567             c.insets = compInsets;
2568             c.weighty = 1.0;
2569 
2570             cbJobSheets = createCheckBox("checkbox.jobsheets", this);
2571             c.anchor = GridBagConstraints.LINE_START;
2572             addToGB(cbJobSheets, this, gridbag, c);
2573 
2574             JPanel pnlTop = new JPanel();
2575             lblPriority = new JLabel(getMsg("label.priority"), JLabel.TRAILING);
2576             lblPriority.setDisplayedMnemonic(getMnemonic("label.priority"));
2577 
2578             pnlTop.add(lblPriority);
2579             snModel = new SpinnerNumberModel(1, 1, 100, 1);
2580             spinPriority = new JSpinner(snModel);
2581             lblPriority.setLabelFor(spinPriority);
2582             // REMIND
2583             ((JSpinner.NumberEditor)spinPriority.getEditor()).getTextField().setColumns(3);
2584             spinPriority.addChangeListener(this);
2585             pnlTop.add(spinPriority);
2586             c.anchor = GridBagConstraints.LINE_END;
2587             c.gridwidth = GridBagConstraints.REMAINDER;
2588             pnlTop.getAccessibleContext().setAccessibleName(
2589                                        getMsg("label.priority"));
2590             addToGB(pnlTop, this, gridbag, c);
2591 
2592             c.fill = GridBagConstraints.HORIZONTAL;
2593             c.anchor = GridBagConstraints.CENTER;
2594             c.weightx = 0.0;
2595             c.gridwidth = 1;
2596             char jmnemonic = getMnemonic("label.jobname");
2597             lblJobName = new JLabel(getMsg("label.jobname"), JLabel.TRAILING);
2598             lblJobName.setDisplayedMnemonic(jmnemonic);
2599             addToGB(lblJobName, this, gridbag, c);
2600             c.weightx = 1.0;
2601             c.gridwidth = GridBagConstraints.REMAINDER;
2602             tfJobName = new JTextField();
2603             lblJobName.setLabelFor(tfJobName);
2604             tfJobName.addFocusListener(this);
2605             tfJobName.setFocusAccelerator(jmnemonic);
2606             tfJobName.getAccessibleContext().setAccessibleName(
2607                                              getMsg("label.jobname"));
2608             addToGB(tfJobName, this, gridbag, c);
2609 
2610             c.weightx = 0.0;
2611             c.gridwidth = 1;
2612             char umnemonic = getMnemonic("label.username");
2613             lblUserName = new JLabel(getMsg("label.username"), JLabel.TRAILING);
2614             lblUserName.setDisplayedMnemonic(umnemonic);
2615             addToGB(lblUserName, this, gridbag, c);
2616             c.gridwidth = GridBagConstraints.REMAINDER;
2617             tfUserName = new JTextField();
2618             lblUserName.setLabelFor(tfUserName);
2619             tfUserName.addFocusListener(this);
2620             tfUserName.setFocusAccelerator(umnemonic);
2621             tfUserName.getAccessibleContext().setAccessibleName(
2622                                              getMsg("label.username"));
2623             addToGB(tfUserName, this, gridbag, c);
2624         }
2625 
2626         public void actionPerformed(ActionEvent e) {
2627             if (cbJobSheets.isSelected()) {
2628                 asCurrent.add(JobSheets.STANDARD);
2629             } else {
2630                 asCurrent.add(JobSheets.NONE);
2631             }
2632         }
2633 
2634         public void stateChanged(ChangeEvent e) {
2635             asCurrent.add(new JobPriority(snModel.getNumber().intValue()));
2636         }
2637 
2638         public void focusLost(FocusEvent e) {
2639             Object source = e.getSource();
2640 
2641             if (source == tfJobName) {
2642                 asCurrent.add(new JobName(tfJobName.getText(),
2643                                           Locale.getDefault()));
2644             } else if (source == tfUserName) {
2645                 asCurrent.add(new RequestingUserName(tfUserName.getText(),
2646                                                      Locale.getDefault()));
2647             }
2648         }
2649 
2650         public void focusGained(FocusEvent e) {}
2651 
2652         public void updateInfo() {
2653             Class jsCategory = JobSheets.class;
2654             Class jpCategory = JobPriority.class;
2655             Class jnCategory = JobName.class;
2656             Class unCategory = RequestingUserName.class;
2657             boolean jsSupported = false;
2658             boolean jpSupported = false;
2659             boolean jnSupported = false;
2660             boolean unSupported = false;
2661 
2662             // setup JobSheets checkbox
2663             if (psCurrent.isAttributeCategorySupported(jsCategory)) {
2664                 jsSupported = true;
2665             }
2666             JobSheets js = (JobSheets)asCurrent.get(jsCategory);
2667             if (js == null) {
2668                 js = (JobSheets)psCurrent.getDefaultAttributeValue(jsCategory);
2669                 if (js == null) {
2670                     js = JobSheets.NONE;
2671                 }
2672             }
2673             cbJobSheets.setSelected(js != JobSheets.NONE);
2674             cbJobSheets.setEnabled(jsSupported);
2675 
2676             // setup JobPriority spinner
2677             if (!isAWT && psCurrent.isAttributeCategorySupported(jpCategory)) {
2678                 jpSupported = true;
2679             }
2680             JobPriority jp = (JobPriority)asCurrent.get(jpCategory);
2681             if (jp == null) {
2682                 jp = (JobPriority)psCurrent.getDefaultAttributeValue(jpCategory);
2683                 if (jp == null) {
2684                     jp = new JobPriority(1);
2685                 }
2686             }
2687             int value = jp.getValue();
2688             if ((value < 1) || (value > 100)) {
2689                 value = 1;
2690             }
2691             snModel.setValue(new Integer(value));
2692             lblPriority.setEnabled(jpSupported);
2693             spinPriority.setEnabled(jpSupported);
2694 
2695             // setup JobName text field
2696             if (psCurrent.isAttributeCategorySupported(jnCategory)) {
2697                 jnSupported = true;
2698             }
2699             JobName jn = (JobName)asCurrent.get(jnCategory);
2700             if (jn == null) {
2701                 jn = (JobName)psCurrent.getDefaultAttributeValue(jnCategory);
2702                 if (jn == null) {
2703                     jn = new JobName("", Locale.getDefault());
2704                 }
2705             }
2706             tfJobName.setText(jn.getValue());
2707             tfJobName.setEnabled(jnSupported);
2708             lblJobName.setEnabled(jnSupported);
2709 
2710             // setup RequestingUserName text field
2711             if (!isAWT && psCurrent.isAttributeCategorySupported(unCategory)) {
2712                 unSupported = true;
2713             }
2714             RequestingUserName un = (RequestingUserName)asCurrent.get(unCategory);
2715             if (un == null) {
2716                 un = (RequestingUserName)psCurrent.getDefaultAttributeValue(unCategory);
2717                 if (un == null) {
2718                     un = new RequestingUserName("", Locale.getDefault());
2719                 }
2720             }
2721             tfUserName.setText(un.getValue());
2722             tfUserName.setEnabled(unSupported);
2723             lblUserName.setEnabled(unSupported);
2724         }
2725     }
2726 
2727 
2728 
2729 
2730     /**
2731      * A special widget that groups a JRadioButton with an associated icon,
2732      * placed to the left of the radio button.
2733      */
2734     private class IconRadioButton extends JPanel {
2735 
2736         private JRadioButton rb;
2737         private JLabel lbl;
2738 
2739         public IconRadioButton(String key, String img, boolean selected,
2740                                ButtonGroup bg, ActionListener al)
2741         {
2742             super(new FlowLayout(FlowLayout.LEADING));
2743             final URL imgURL = getImageResource(img);
2744             Icon icon = (Icon)java.security.AccessController.doPrivileged(
2745                                  new java.security.PrivilegedAction() {
2746                 public Object run() {
2747                     Icon icon = new ImageIcon(imgURL);
2748                     return icon;
2749                 }
2750             });
2751             lbl = new JLabel(icon);
2752             add(lbl);
2753 
2754             rb = createRadioButton(key, al);
2755             rb.setSelected(selected);
2756             addToBG(rb, this, bg);
2757         }
2758 
2759         public void addActionListener(ActionListener al) {
2760             rb.addActionListener(al);
2761         }
2762 
2763         public boolean isSameAs(Object source) {
2764             return (rb == source);
2765         }
2766 
2767         public void setEnabled(boolean enabled) {
2768             rb.setEnabled(enabled);
2769             lbl.setEnabled(enabled);
2770         }
2771 
2772         public boolean isSelected() {
2773             return rb.isSelected();
2774         }
2775 
2776         public void setSelected(boolean selected) {
2777             rb.setSelected(selected);
2778         }
2779     }
2780 
2781     /**
2782      * Similar in functionality to the default JFileChooser, except this
2783      * chooser will pop up a "Do you want to overwrite..." dialog if the
2784      * user selects a file that already exists.
2785      */
2786     private class ValidatingFileChooser extends JFileChooser {
2787         public void approveSelection() {
2788             File selected = getSelectedFile();
2789             boolean exists;
2790 
2791             try {
2792                 exists = selected.exists();
2793             } catch (SecurityException e) {
2794                 exists = false;
2795             }
2796 
2797             if (exists) {
2798                 int val;
2799                 val = JOptionPane.showConfirmDialog(this,
2800                                                     getMsg("dialog.overwrite"),
2801                                                     getMsg("dialog.owtitle"),
2802                                                     JOptionPane.YES_NO_OPTION);
2803                 if (val != JOptionPane.YES_OPTION) {
2804                     return;
2805                 }
2806             }
2807 
2808             try {
2809                 if (selected.createNewFile()) {
2810                     selected.delete();
2811                 }
2812             }  catch (IOException ioe) {
2813                 JOptionPane.showMessageDialog(this,
2814                                    getMsg("dialog.writeerror")+" "+selected,
2815                                    getMsg("dialog.owtitle"),
2816                                    JOptionPane.WARNING_MESSAGE);
2817                 return;
2818             } catch (SecurityException se) {
2819                 //There is already file read/write access so at this point
2820                 // only delete access is denied.  Just ignore it because in
2821                 // most cases the file created in createNewFile gets
2822                 // overwritten anyway.
2823             }
2824             File pFile = selected.getParentFile();
2825             if ((selected.exists() &&
2826                       (!selected.isFile() || !selected.canWrite())) ||
2827                      ((pFile != null) &&
2828                       (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {
2829                 JOptionPane.showMessageDialog(this,
2830                                    getMsg("dialog.writeerror")+" "+selected,
2831                                    getMsg("dialog.owtitle"),
2832                                    JOptionPane.WARNING_MESSAGE);
2833                 return;
2834             }
2835 
2836             super.approveSelection();
2837         }
2838     }
2839 }